configured.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. <script setup lang="ts">
  2. import { type VxeFormListeners, type VxeFormProps, type VxeGridInstance, type VxeGridListeners, type VxeGridProps, VxeUI } from 'vxe-pc-ui';
  3. import { usePagination, useRequest } from 'alova/client';
  4. import { notification } from 'ant-design-vue';
  5. import { getDictionaryMethod } from '@/request/api/dictionary.api';
  6. import dayjs from 'dayjs';
  7. import EditConfigured from '@/service/EditConfigured.vue';
  8. // model
  9. import type { ConditioningSchemeModel, ConditioningSchemeQuery } from '@/model/care.model';
  10. // 接口数据
  11. import { getConditioningSchemeMethod } from '@/request/api/care.api';
  12. import { branchMethod } from '@/request/api/system.api';
  13. // 获取组织名称
  14. const { data: branch, loading: branchLoading } = useRequest(branchMethod(0, 1, 1));
  15. const model = shallowRef<ConditioningSchemeQuery>();
  16. // 获取设备类型
  17. const deviceTypes = ref<{ id: string; name: string }[]>([]);
  18. const deviceTypesLoading = ref(false);
  19. // 日期验证
  20. const configTimeStart = ref<string>('');
  21. const configTimeEnd = ref<string>('');
  22. // 禁用结束时间的日期(早于开始时间的日期)
  23. function disabledEndDate(current: any) {
  24. if (!configTimeStart.value) return false;
  25. return current && current < dayjs(configTimeStart.value);
  26. }
  27. const searchFormProps = reactive<VxeFormProps<ConditioningSchemeQuery>>({
  28. titleWidth: 100,
  29. titleAlign: 'right',
  30. titleColon: true,
  31. data: {
  32. // isConfig: 'Y',
  33. },
  34. items: [
  35. {
  36. field: 'orgId',
  37. title: '组织名称',
  38. span: 5,
  39. itemRender: {
  40. name: 'VxeTreeSelect',
  41. props: {
  42. loading: computed(() => branchLoading.value),
  43. options: computed(() => branch.value),
  44. optionProps: { value: 'id', label: 'label' },
  45. clearable: true,
  46. },
  47. events: {
  48. change(val: any) {
  49. insArr.value = [];
  50. if (val.data.orgId) {
  51. // 清空表单中的机构名称字段
  52. if (model.value) {
  53. // model.value.institutionId = '';
  54. model.value.insId = '';
  55. }
  56. getInstitution(val.data.orgId);
  57. }
  58. },
  59. },
  60. },
  61. },
  62. {
  63. field: 'insId',
  64. title: '机构名称',
  65. span: 6,
  66. itemRender: {
  67. name: 'VxeTreeSelect',
  68. props: {
  69. loading: computed(() => insLoading.value),
  70. options: computed(() => insArr.value),
  71. optionProps: { value: 'id', label: 'label' },
  72. clearable: true,
  73. },
  74. },
  75. },
  76. {
  77. field: 'configTimeStart',
  78. title: '配置时间',
  79. span: 10,
  80. slots: {
  81. default: 'createTimes',
  82. },
  83. },
  84. {
  85. field: 'isConfig',
  86. title: '是否配置',
  87. span: 6,
  88. itemRender: {
  89. name: 'VxeRadioGroup',
  90. options: [
  91. { label: '已配置', value: 'Y' },
  92. { label: '未配置', value: 'N' },
  93. ],
  94. props: {
  95. strict: false,
  96. },
  97. },
  98. },
  99. {
  100. span: 3,
  101. itemRender: {
  102. name: 'VxeButtonGroup',
  103. options: [
  104. { name: 'submits', type: 'submit', content: '查询', status: 'primary' },
  105. { name: 'reset', type: 'reset', content: '清空', status: 'warning' },
  106. ],
  107. },
  108. },
  109. ],
  110. });
  111. const searchFormEmits: VxeFormListeners<ConditioningSchemeQuery> = {
  112. // 查询设备登记
  113. submit({ data }) {
  114. model.value = {
  115. ...data,
  116. configTimeStart: configTimeStart.value ? dayjs(configTimeStart.value).format('YYYY-MM-DD HH:mm:ss') : '',
  117. configTimeEnd: configTimeEnd.value ? dayjs(configTimeEnd.value).format('YYYY-MM-DD HH:mm:ss') : '',
  118. } as any;
  119. },
  120. // 重置
  121. reset({ data }) {
  122. model.value = { ...data } as any;
  123. configTimeStart.value = '';
  124. configTimeEnd.value = '';
  125. },
  126. };
  127. // 设备列表
  128. const gridRef = ref<VxeGridInstance<ConditioningSchemeModel>>();
  129. const gridOptions = reactive<VxeGridProps<ConditioningSchemeModel>>({
  130. id: 'tag-list',
  131. border: true,
  132. showOverflow: true,
  133. height: 'auto',
  134. autoResize: false,
  135. syncResize: true,
  136. scrollY: { enabled: true, gt: 0 },
  137. toolbarConfig: {
  138. custom: true,
  139. zoom: true,
  140. slots: {
  141. tools: 'toolbar-extra',
  142. },
  143. },
  144. columnConfig: {
  145. resizable: true,
  146. },
  147. customConfig: {
  148. storage: true,
  149. },
  150. columns: [
  151. { field: 'orgName', title: '组织名称' },
  152. { field: 'insName', title: '机构名称' },
  153. {
  154. field: 'isHaveForInfer',
  155. title: '是否定制项目',
  156. slots: {
  157. default: 'isHaveForInferSlot',
  158. },
  159. },
  160. // { field: 'forInferCount', title: '定制项目数' },
  161. {
  162. field: 'isConfig',
  163. title: '是否配置',
  164. slots: {
  165. default: 'isConfiguredSlot',
  166. },
  167. },
  168. { field: 'updateBy', title: '修改人' },
  169. { field: 'updateTime', title: '修改时间' },
  170. {
  171. field: 'action',
  172. title: '操作',
  173. align: 'center',
  174. width: 120,
  175. showOverflow: false,
  176. cellRender: {
  177. name: 'VxeButtonGroup',
  178. props: {
  179. mode: 'text',
  180. },
  181. options: [{ content: '编辑', status: 'primary', name: 'EditConfigured' }],
  182. events: {
  183. click({ row, rowIndex }, { name }) {
  184. let method;
  185. if (name === 'EditConfigured') {
  186. method = editConfigured;
  187. }
  188. method?.(row, rowIndex);
  189. },
  190. },
  191. },
  192. },
  193. ],
  194. data: [],
  195. });
  196. const gridEvents: VxeGridListeners = {};
  197. // 获取设备分页列表
  198. const { loading, page, pageSize, total, onSuccess, replace, refresh, remove, send: sendRefresh } = usePagination((page, size) => getConditioningSchemeMethod(page, size, model.value), {
  199. initialData: { data: [], total: 0 },
  200. initialPage: 1,
  201. initialPageSize: 100,
  202. watchingStates: [model],
  203. immediate: false,
  204. });
  205. onSuccess(({ data: { data } }) => {
  206. gridRef.value?.loadData(data);
  207. });
  208. // 获取设备类型
  209. async function getDeviceType() {
  210. deviceTypesLoading.value = true;
  211. const res = await getDictionaryMethod('fdhb_device_type');
  212. if (res && res.length > 0) {
  213. deviceTypes.value = res.map((item: any) => ({
  214. id: item.label,
  215. name: item.label,
  216. }));
  217. }
  218. deviceTypesLoading.value = false;
  219. }
  220. const insLoading = ref(false);
  221. const insArr = ref<any[]>([]);
  222. async function getInstitution(orgId: string | number) {
  223. insLoading.value = true;
  224. const res = await branchMethod(1, 0, Number(orgId));
  225. if (res && res.length > 0) {
  226. insArr.value = res;
  227. }
  228. insLoading.value = false;
  229. }
  230. onMounted(() => {
  231. getDeviceType();
  232. model.value = toRaw(searchFormProps.data);
  233. });
  234. // 编辑配置
  235. function editConfigured(model?: ConditioningSchemeModel, index?: number) {
  236. VxeUI.modal.open({
  237. title: model?.id ? `修改配置` : `新增配置`,
  238. height: 750,
  239. width: 1100,
  240. escClosable: true,
  241. destroyOnClose: true,
  242. id: `configured-modal`,
  243. remember: true,
  244. storage: true,
  245. slots: {
  246. default() {
  247. return h(EditConfigured, <any>{
  248. data: model,
  249. onSubmit(success: boolean, data?: ConditioningSchemeModel) {
  250. console.log(success, 'success');
  251. // 只有成功时才刷新列表
  252. if (success) {
  253. // 使用 sendRefresh 强制刷新,避免页面闪烁
  254. sendRefresh();
  255. }
  256. VxeUI.modal.close(`configured-modal`);
  257. },
  258. });
  259. },
  260. },
  261. });
  262. }
  263. </script>
  264. <template>
  265. <div class="page-container flex flex-col">
  266. <header class="flex-none mt-4">
  267. <vxe-form v-bind="searchFormProps" v-on="searchFormEmits">
  268. <template #createTimes>
  269. <div class="date-range-container">
  270. <a-date-picker v-model:value="configTimeStart" placeholder="请选择开始时间" style="flex: 1" :show-time="{ format: 'HH:mm' }" />
  271. <span class="date-separator">至</span>
  272. <a-date-picker v-model:value="configTimeEnd" placeholder="请选择结束时间" style="flex: 1" :disabledDate="disabledEndDate" :show-time="{ format: 'HH:mm' }" />
  273. </div>
  274. </template>
  275. </vxe-form>
  276. </header>
  277. <main class="flex-auto overflow-hidden">
  278. <vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents" :loading="loading">
  279. <template #isConfiguredSlot="{ row }">
  280. <div :class="row.isConfig === 'Y' ? 'text-green-500' : 'text-red-500'">{{ row.isConfig === 'Y' ? '已配置' : '未配置' }}</div>
  281. </template>
  282. <template #isHaveForInferSlot="{ row }">
  283. <div>{{ row.isHaveForInfer === 'Y' ? '定制' : '无定制' }}</div>
  284. </template>
  285. <template #toolbar-extra>
  286. <vxe-button style="margin-right: 12px" icon="vxe-icon-repeat" circle @click="refresh(page)"></vxe-button>
  287. </template>
  288. </vxe-grid>
  289. </main>
  290. <footer class="flex-none">
  291. <vxe-pager
  292. v-model:current-page="page"
  293. v-model:page-size="pageSize"
  294. :total="total"
  295. :layouts="['Home', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'End', 'Sizes', 'FullJump', 'Total']"
  296. />
  297. </footer>
  298. </div>
  299. </template>
  300. <style scoped lang="scss">
  301. .page-container {
  302. padding: 0 24px;
  303. max-height: var(--page-main-container);
  304. }
  305. .date-range-container {
  306. display: flex;
  307. align-items: center;
  308. gap: 12px;
  309. width: 100%;
  310. .vxe-input {
  311. flex: 1;
  312. min-width: 0;
  313. }
  314. .date-separator {
  315. color: #666;
  316. font-size: 14px;
  317. font-weight: 500;
  318. white-space: nowrap;
  319. padding: 0 8px;
  320. }
  321. }
  322. </style>