@yxhl/specter-pui-vtk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +122 -0
  2. package/dist/specter-pui-vtk.css +1 -0
  3. package/dist/specter-pui.es.js +3289 -0
  4. package/dist/specter-pui.es.js.map +1 -0
  5. package/dist/specter-pui.umd.js +2 -0
  6. package/dist/specter-pui.umd.js.map +1 -0
  7. package/package.json +65 -0
  8. package/src/assets/css/globals.scss +250 -0
  9. package/src/assets/css/index.scss +271 -0
  10. package/src/assets/css/settings.scss +10 -0
  11. package/src/assets/css/variables.scss +0 -0
  12. package/src/assets/icon/logo.svg +9 -0
  13. package/src/assets/img/background.png +0 -0
  14. package/src/assets/img/dtx.png +0 -0
  15. package/src/commons/filters/dictionary.js +75 -0
  16. package/src/commons/filters/format.js +112 -0
  17. package/src/commons/filters/mask.js +25 -0
  18. package/src/commons/index.js +17 -0
  19. package/src/commons/request.js +89 -0
  20. package/src/commons/storage.js +41 -0
  21. package/src/commons/themes.js +153 -0
  22. package/src/commons/validation.js +72 -0
  23. package/src/components/README.md +35 -0
  24. package/src/components/assembly/VtkArea.vue +259 -0
  25. package/src/components/assembly/VtkCheckbox.vue +168 -0
  26. package/src/components/assembly/VtkCount.vue +403 -0
  27. package/src/components/assembly/VtkDatePicker.vue +326 -0
  28. package/src/components/assembly/VtkEmpty.vue +107 -0
  29. package/src/components/assembly/VtkFab.vue +78 -0
  30. package/src/components/assembly/VtkFormItem.vue +166 -0
  31. package/src/components/assembly/VtkImg.vue +372 -0
  32. package/src/components/assembly/VtkPage.vue +156 -0
  33. package/src/components/assembly/VtkPdf.vue +424 -0
  34. package/src/components/assembly/VtkProj.vue +539 -0
  35. package/src/components/assembly/VtkRadio.vue +82 -0
  36. package/src/components/assembly/VtkSearch.vue +145 -0
  37. package/src/components/assembly/VtkSelect.vue +104 -0
  38. package/src/components/assembly/VtkStepper.vue +160 -0
  39. package/src/components/message/alert.vue +31 -0
  40. package/src/components/message/confirm.vue +44 -0
  41. package/src/components/message/index.js +55 -0
  42. package/src/components/message/loading.vue +33 -0
  43. package/src/components/message/prompt.vue +57 -0
  44. package/src/components/message/toast.vue +45 -0
  45. package/src/components/message/vtkMessage.vue +27 -0
  46. package/src/composables/useMixins.js +2 -0
  47. package/src/composables/usePage.js +311 -0
  48. package/src/index.js +109 -0
  49. package/src/stores/message.js +79 -0
@@ -0,0 +1,539 @@
1
+ <template>
2
+ <VContainer fluid class="vtk-height pa-0">
3
+ <VTabs v-model="examType" bg-color="transparent" height="38" style="width: 100%">
4
+ <VTab @click="examAll()">
5
+ <span><VIcon size="16" class="mr-1">mdi-clipboard-arrow-up</VIcon>全部</span>
6
+ </VTab>
7
+ <VTab @click="list">
8
+ <span><VIcon size="16" class="mr-1">mdi-star-circle</VIcon>审核</span>
9
+ </VTab>
10
+ <VTab @click="tip">
11
+ <span><VIcon size="16" class="mr-1">mdi-hand-heart</VIcon>实施</span>
12
+ </VTab>
13
+ <VTab @click="tip">
14
+ <span><VIcon size="16" class="mr-1">mdi-comment-minus</VIcon>评价</span>
15
+ </VTab>
16
+ <VTab @click="tip">
17
+ <span><VIcon size="16" class="mr-1">mdi-credit-card-check</VIcon>支付</span>
18
+ </VTab>
19
+ <VSpacer />
20
+ </VTabs>
21
+ <VCard elevation="0" style="min-height: calc(100% - 38px)">
22
+ <VCardTitle style="height: 88px">
23
+ <VMenu v-if="examType == '0' && (expShow || user.grole >= 2)" offset-y :close-on-content-click="false" location="bottom">
24
+ <template v-slot:activator="{ props }">
25
+ <VBtn @click="exv" v-bind="props" class="bg-orange text-white mr-3" elevation="0">
26
+ <VIcon>mdi-export-variant</VIcon> 导出
27
+ </VBtn>
28
+ </template>
29
+ <VCard class="pa-3" width="300">
30
+ <div>
31
+ <p class="mb-2 font-weight-bold">公共字段</p>
32
+ <p class="text-caption">
33
+ 服务事项
34
+ <span class="text-grey mx-1">|</span>
35
+ 所属区县
36
+ <span class="text-grey mx-1">|</span>
37
+ 所属镇街
38
+ <span class="text-grey mx-1">|</span>
39
+ 所属社区
40
+ <span class="text-grey mx-1">|</span>
41
+ 申请人证件号
42
+ <span class="text-grey mx-1">|</span>
43
+ 申请人姓名
44
+ <span class="text-grey mx-1">|</span>
45
+ 残疾人证号
46
+ <span class="text-grey mx-1">|</span>
47
+ 性别
48
+ <span class="text-grey mx-1">|</span>
49
+ 申请人手机号
50
+ <span class="text-grey mx-1">|</span>
51
+ 补助金额
52
+ <span class="text-grey mx-1">|</span>
53
+ 开户银行
54
+ <span class="text-grey mx-1">|</span>
55
+ 银行卡号
56
+ <span class="text-grey mx-1">|</span>
57
+ 收款人姓名
58
+ <span class="text-grey mx-1">|</span>
59
+ 收款人证件号
60
+ <span class="text-grey mx-1">|</span>
61
+ 状态
62
+ </p>
63
+ <VDivider />
64
+ <div v-if="queryParam.servState == '1' && projName">
65
+ <VCardTitle class="pa-0 text-body-1 mb-2 pt-3">
66
+ <VCheckbox @update:model-value="chkAll" v-model="select" class="mt-n1" hide-details>
67
+ <template v-slot:append>
68
+ <VIcon class="active" style="display: none" size="18">mdi-check-circle</VIcon>
69
+ </template>
70
+ </VCheckbox>
71
+ <span class="font-weight-bold ml-n2">拓展字段</span>
72
+ <VSpacer />
73
+ </VCardTitle>
74
+ <p v-if="formArr?.length > 0" class="mb-0 expBox">
75
+ <VCheckbox
76
+ v-for="(item, index) in formArr"
77
+ :key="index"
78
+ v-model="expArr"
79
+ :label="item.label"
80
+ :value="{ name: item.name, valu: `${item.label}${item.valu && item.valu.includes('/') && item.valu.includes(':') ? `(${item.valu})` : ''}` }"
81
+ class="mt-2"
82
+ multiple
83
+ hide-details>
84
+ <template v-slot:append>
85
+ <VIcon class="active" style="display: none" size="18">mdi-check-circle</VIcon>
86
+ </template>
87
+ </VCheckbox>
88
+ </p>
89
+ <p v-else class="text-red text-body-2 mb-2">{{ projTip }}</p>
90
+ </div>
91
+ <div v-else>
92
+ <p class="mb-3 pt-3 font-weight-bold">导出提示</p>
93
+ <p :class="queryParam.servState == '1' ? 'text-green' : 'text-red'" class="mb-3 text-body-2">
94
+ <VIcon size="20" :color="queryParam.servState == '1' ? 'green' : 'red'">{{ queryParam.servState == "1" ? "mdi-progress-check" : "mdi-progress-close" }}</VIcon>
95
+ 导出数据仅限已服务信息
96
+ </p>
97
+ <p class="mb-2 text-body-2" :class="projName ? 'text-green' : 'text-red'">
98
+ <VIcon size="20" :color="projName ? 'green' : 'red'">{{ projName ? "mdi-progress-check" : "mdi-progress-close" }}</VIcon>
99
+ 至少选择一个服务事项导出
100
+ </p>
101
+ </div>
102
+ </div>
103
+ <VBtn @click="exp" :disabled="!(projName && queryParam.servState == '1')" elevation="0" color="primary" class="vtk-width mt-3" size="small">确认导出</VBtn>
104
+ </VCard>
105
+ </VMenu>
106
+ <VSpacer />
107
+ <vtk-search v-model="queryParam" @search="searchQuery" placeholder="输入姓名/身份证查询" :styles="'width:320px'">
108
+ <VTextField @click="searchProj" v-model="projName" :title="projName" clearable readonly variant="outlined" placeholder="服务事项" density="compact" hide-details class="my-3"></VTextField>
109
+ <vtk-area-tabs v-model="queryParam.areaCode" :styles="'width:100%'" />
110
+ <div v-if="examType == 0" style="width: 400px">
111
+ <vtk-select v-model="queryParam.servState" @change="searchQuery" placeholder="服务状态" list="0:服务中/1:已服务" class="mt-3 d-inline-block mr-3 mb-3" style="width: calc(50% - 6px)" hide-details density="compact" variant="outlined" clearable></vtk-select>
112
+ <vtk-select v-model="queryParam.rateState" @change="searchQuery" placeholder="是否评价" list="0:未评价/1:已评价" class="mt-3 d-inline-block" style="width: calc(50% - 6px)" hide-details density="compact" variant="outlined" clearable></vtk-select>
113
+ <VMenu v-model="menu" :close-on-content-click="false" transition="scale-transition" location="bottom" max-width="290px" min-width="auto">
114
+ <template v-slot:activator="{ props }">
115
+ <VTextField v-model="queryParam.servFtime0" placeholder="开始时间" persistent-hint append-icon="mdi-calendar" class="d-inline-block" style="width: calc(50% - 16px)" hide-details variant="outlined" density="compact" v-bind="props"></VTextField>
116
+ </template>
117
+ <VDatePicker v-model="queryParam.servFtime0" title="" @update:model-value="menu = false" view-mode="month" :max="queryParam.servFtime1"></VDatePicker>
118
+ </VMenu>
119
+ <span class="mx-2">至</span>
120
+ <VMenu v-model="menu1" :close-on-content-click="false" transition="scale-transition" location="bottom" max-width="290px" min-width="auto">
121
+ <template v-slot:activator="{ props }">
122
+ <VTextField v-model="queryParam.servFtime1" placeholder="结束时间" persistent-hint append-icon="mdi-calendar" class="d-inline-block" style="width: calc(50% - 15px)" hide-details variant="outlined" density="compact" v-bind="props"></VTextField>
123
+ </template>
124
+ <VDatePicker v-model="queryParam.servFtime1" title="" @update:model-value="menu1 = false" view-mode="month" :min="queryParam.servFtime0"></VDatePicker>
125
+ </VMenu>
126
+ <vtk-select v-if="queryParam.servState == '1'" @change="searchQuery" v-model="queryParam.state" class="mt-3" placeholder="状态" list="1:已通过/0:未通过" hide-details density="compact" variant="outlined" clearable></vtk-select>
127
+ </div>
128
+ </vtk-search>
129
+ </VCardTitle>
130
+ <VCardText>
131
+ <VDataTable :headers="headers" :items="desserts" :loading="loading" disable-pagination loading-text="数据加载中..." class="elevation-0" hide-default-footer>
132
+ <template v-slot:[`item.objsName`]="{ item }">
133
+ <div class="py-3" @click="toView(item)">
134
+ <p class="vtk-cursor-pointer text-body-1 font-weight-bold mb-2">
135
+ {{ item.objsName }}<span class="ml-3 text-grey">{{ formatIdCard(item.objsCode) }} </span>
136
+ <VChip class="ml-2" :class="getServStateClass(item.servState)" size="small" label>{{ getServStateText(item.servState) }} </VChip>
137
+ <VChip class="ml-2" :class="getRateStateClass(item.rateState)" size="small" label>{{ getRateStateText(item.rateState) }} </VChip>
138
+ </p>
139
+ <span>
140
+ <VChip class="bg-orange-lighten-5 text-orange" size="small" label> 事项编码:{{ item.projCode }} </VChip>
141
+ <VChip class="text-primary ml-2" size="small" label> 办件编号:{{ item.inst }} </VChip>
142
+ </span>
143
+ <div class="mb-0 mt-2 text-grey">
144
+ <div class="d-inline-block" style="min-width: 320px">
145
+ 服务名称:<span class="text-blue">{{ item.projName }}</span>
146
+ </div>
147
+ <span>补助金额:{{ item.servAmts }}元</span>
148
+ </div>
149
+ <VRow class="py-2" style="color: #979797c2">
150
+ <VCol style="max-width: 320px">
151
+ <span :title="item.name" class="d-block" style="color: #979797c2">申请途径:{{ getServFromText(item.servFrom) }}</span>
152
+ </VCol>
153
+ <VCol>
154
+ <span :title="item.name" class="d-block" style="color: #979797c2">申请时间:{{ formatDate(item.servCtime) }}</span>
155
+ </VCol>
156
+ </VRow>
157
+ </div>
158
+ </template>
159
+ <template v-slot:[`item.actions`]="{ item }">
160
+ <a v-if="item.state != 0 && user.areacode?.length == 6 && item.servState == '1'" @click="del(item)" class="text-error" text>作废</a>
161
+ <div v-if="item.state == 0" class="state_btg text-right"></div>
162
+ </template>
163
+ <template v-slot:no-data>
164
+ <vtk-empty />
165
+ </template>
166
+ </VDataTable>
167
+ <vtk-page :page-sizes="[10, 20]" :page-data="pageData" @pageChange="getListPage" />
168
+ </VCardText>
169
+ </VCard>
170
+ <!-- 服务事项 -->
171
+ <vtk-proj ref="MprojRef" @toPage="toPage" />
172
+ <!-- 删除服务 -->
173
+ <VDialog v-model="dialog" persistent max-width="500">
174
+ <VCard>
175
+ <VCardTitle>作废</VCardTitle>
176
+ <VCardText class="pb-0">
177
+ 服务作废后将无法恢复,请谨慎操作!!!
178
+ <VTextarea v-model="delNote" variant="outlined" label="请填写作废原因" class="mt-3" hide-details density="compact"></VTextarea>
179
+ </VCardText>
180
+ <VCardActions>
181
+ <VSpacer />
182
+ <VBtn @click="dialog = false" variant="text">取消</VBtn>
183
+ <VBtn @click="delSave(true)" variant="text">确定</VBtn>
184
+ </VCardActions>
185
+ </VCard>
186
+ </VDialog>
187
+ </VContainer>
188
+ </template>
189
+
190
+ <script setup>
191
+ import { ref, watch, onActivated } from 'vue';
192
+ import { useMixins } from '../../composables/useMixins'
193
+ import Request from '../../commons/request';
194
+
195
+ // 定义组件选项
196
+ defineOptions({
197
+ name: 'VtkProj',
198
+ inheritAttrs: false,
199
+ });
200
+
201
+ // 表单列表 URL 配置
202
+ const urlConfig = {
203
+ list: '/kudas/xfwez/page',
204
+ exp: '/kudas/xfwez/exp',
205
+ delete: '/kudas/xfwez/delete'
206
+ };
207
+
208
+ // 使用 mixins composable
209
+ const {
210
+ pageData,
211
+ getListPage,
212
+ loadPage,
213
+ expData,
214
+ desserts,
215
+ loading,
216
+ queryParam: mixinQueryParam,
217
+ url
218
+ } = useMixins(urlConfig);
219
+
220
+ // 合并 queryParam
221
+ Object.assign(mixinQueryParam, {});
222
+ const queryParam = mixinQueryParam;
223
+
224
+ // 响应式数据
225
+ const user = ref({});
226
+ const examType = ref(1);
227
+
228
+ const headers = ref([
229
+ { title: '事项', key: 'objsName', sortable: false },
230
+ { title: '状态', key: 'actions', sortable: false, align: 'end' },
231
+ ]);
232
+
233
+ // 主题筛选
234
+ const permits = ref([]);
235
+ const projName = ref('');
236
+ const projTip = ref('');
237
+ const bizType = ref('');
238
+ const expShow = ref(false);
239
+ const menu = ref(false);
240
+ const menu1 = ref(false);
241
+
242
+ // 导出
243
+ const expArr = ref([]);
244
+ const formArr = ref([]);
245
+ const select = ref(false);
246
+ const selectAll = ref([]);
247
+
248
+ // 删除
249
+ const dialog = ref(false);
250
+ const delItem = ref({});
251
+ const delNote = ref('');
252
+
253
+ // ref
254
+ const MprojRef = ref(null);
255
+
256
+ // 监听器
257
+ watch(expArr, (newVal) => {
258
+ if (newVal.length < selectAll.value.length) {
259
+ select.value = false;
260
+ }
261
+ }, { deep: true });
262
+
263
+ // 生命周期
264
+ onActivated(() => {
265
+ alert('onActivated');
266
+ list();
267
+ // 监听接收消息,刷新页面
268
+ window.addEventListener('storage', (event) => {
269
+ if (JSON.parse(event?.newValue)?.fwez) {
270
+ examType.value == '0' ? examAll() : loadPage();
271
+ }
272
+ });
273
+ });
274
+
275
+ // 方法
276
+ // 全选
277
+ const chkAll = (e) => {
278
+ expArr.value = e ? selectAll.value : [];
279
+ };
280
+
281
+ const list = () => {
282
+ bizType.value = '';
283
+ expShow.value = false;
284
+ user.value = getUserInfo();
285
+ permits.value = [];
286
+ user.value.permits?.forEach((e) => {
287
+ if (e.permNkey.includes('biz_type')) {
288
+ permits.value.push(e);
289
+ bizType.value = bizType.value + e.permNkey.slice(9) + ',';
290
+ } else if (e.permNkey == 'btn_expo') {
291
+ expShow.value = true;
292
+ }
293
+ });
294
+ queryParam.bizType = bizType.value;
295
+ examWait();
296
+ };
297
+
298
+ // 获取用户信息
299
+ const getUserInfo = () => {
300
+ try {
301
+ // 实际项目中从存储或状态管理获取
302
+ return window.$vtk?.storage?.get('user') || {};
303
+ } catch (error) {
304
+ console.warn('获取用户信息失败:', error);
305
+ return {};
306
+ }
307
+ };
308
+
309
+ // 待审
310
+ const examWait = () => {
311
+ examType.value = 1;
312
+ queryParam.operateLeve = user.value.areacode?.length;
313
+ queryParam.servState = '0';
314
+ loadPage();
315
+ };
316
+
317
+ // 全部审核
318
+ const examAll = () => {
319
+ examType.value = 0;
320
+ delete queryParam.operateLeve;
321
+ delete queryParam.servState;
322
+ loadPage();
323
+ };
324
+
325
+ // 审核
326
+ const toView = (item) => {
327
+ const url = item.servState == 1
328
+ ? `#/mino/suse/view/${item.inst}?ddtab=true`
329
+ : `#/mino/suse/exam/${item.inst}?ddtab=true`;
330
+ window.open(url);
331
+ };
332
+
333
+ // 服务事项查询/筛选
334
+ const searchProj = () => {
335
+ MprojRef.value?.add(queryParam.projCode);
336
+ };
337
+
338
+ const toPage = (param) => {
339
+ projName.value = param.projName;
340
+ queryParam.projCode = param.projCode;
341
+ delete queryParam.bizType;
342
+ searchQuery();
343
+ };
344
+
345
+ const searchQuery = () => {
346
+ // 搜索查询逻辑
347
+ loadPage();
348
+ };
349
+
350
+ // 导出相关
351
+ const exv = () => {
352
+ if (projName.value && queryParam.servState == '1') {
353
+ expArr.value = [];
354
+ formArr.value = [];
355
+ // 显示加载状态
356
+ Request.postJson('/kudas/xfwez/exv', { codex: queryParam }).then((res) => {
357
+ if (res.meta.success) {
358
+ JSON.parse(res.data.formJson).forEach((item) => {
359
+ if (['text', 'radio', 'number', 'time', 'checkbox', 'textarea', 'select'].includes(item.type)) {
360
+ item.value = {
361
+ name: item.name,
362
+ valu: `${item.label}${item.valu && item.valu.includes('/') && item.valu.includes(':') ? `(${item.valu})` : ''}`
363
+ };
364
+ formArr.value.push(item);
365
+ selectAll.value.push(item.value);
366
+ }
367
+ });
368
+ } else {
369
+ projTip.value = res.meta.message;
370
+ }
371
+ });
372
+ }
373
+ };
374
+
375
+ const exp = () => {
376
+ queryParam.fields = expArr.value;
377
+ expData();
378
+ };
379
+
380
+ // 服务作废
381
+ const del = (item) => {
382
+ dialog.value = true;
383
+ delItem.value = item;
384
+ delNote.value = '';
385
+ };
386
+
387
+ const delSave = () => {
388
+ if (delNote.value) {
389
+ Request.postForm(url.delete, { id: delItem.value.id, note: delNote.value }).then((res) => {
390
+ if (res.meta.success) {
391
+ // 显示成功消息
392
+ dialog.value = false;
393
+ loadPage();
394
+ } else {
395
+ // 显示失败消息
396
+ }
397
+ });
398
+ } else {
399
+ // 显示错误消息
400
+ }
401
+ };
402
+
403
+ // 提示
404
+ const tip = () => {
405
+ // 显示应用开发中的消息
406
+ };
407
+
408
+ // 工具函数
409
+ const formatIdCard = (idcard) => {
410
+ if (!idcard) return '';
411
+ return idcard.replace(/^(.{6})(?:\d+)(.{4})$/, '$1********$2');
412
+ };
413
+
414
+ const formatDate = (dateStr) => {
415
+ if (!dateStr) return '';
416
+ const date = new Date(dateStr);
417
+ return `${date.getFullYear()}年${String(date.getMonth() + 1).padStart(2, '0')}月${String(date.getDate()).padStart(2, '0')}日 ${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`;
418
+ };
419
+
420
+ const getServStateClass = (state) => {
421
+ return state == '0' ? 'bg-blue-lighten-5 text-blue' : 'bg-green-lighten-5 text-green';
422
+ };
423
+
424
+ const getServStateText = (state) => {
425
+ return state == '0' ? '服务中' : '已服务';
426
+ };
427
+
428
+ const getRateStateClass = (state) => {
429
+ return state == '0' ? 'bg-orange-lighten-5 text-orange' : 'bg-blue-lighten-5 text-blue';
430
+ };
431
+
432
+ const getRateStateText = (state) => {
433
+ return state == '0' ? '未评价' : '已评价';
434
+ };
435
+
436
+ const getServFromText = (from) => {
437
+ const fromMap = {
438
+ '100': '特殊渠道',
439
+ '101': '移动端代办',
440
+ '102': '窗口端通办',
441
+ '200': '特殊渠道',
442
+ '210': '浙里办App',
443
+ '211': '浙里办微信小程序',
444
+ '212': '浙里办支付宝小程序',
445
+ '230': '微信小程序',
446
+ '240': '支付宝小程序',
447
+ '250': '自助机',
448
+ '300': '特殊渠道',
449
+ '310': '服务外包代办'
450
+ };
451
+ return fromMap[from] || from;
452
+ };
453
+ </script>
454
+
455
+ <style lang="scss" scoped>
456
+ .state_btg {
457
+ padding-right: 0;
458
+ height: 100%;
459
+ background: url(../../assets/img/objs_btg.png) no-repeat;
460
+ background-size: 80px;
461
+ background-position: 100% 100%;
462
+ }
463
+
464
+ :deep(.v-tab) {
465
+ background: url(../../assets/img/tab-2.png) no-repeat;
466
+ background-size: 100% 100%;
467
+ width: 150px;
468
+ margin-left: -11px;
469
+ border-top-left-radius: 20px;
470
+ border-top-right-radius: 20px;
471
+ span {
472
+ z-index: 99;
473
+ }
474
+ }
475
+
476
+ /* Dark theme */
477
+ .v-theme--dark {
478
+ :deep(.v-tab) {
479
+ background: url(../../assets/img/tab-4.png) no-repeat;
480
+ background-size: 100% 100%;
481
+ }
482
+
483
+ :deep(.v-tabs__slider) {
484
+ background: url(../../assets/img/tab-3.png) no-repeat !important;
485
+ background-size: 100% 100% !important;
486
+ }
487
+ }
488
+
489
+ :deep(.v-tab::before),
490
+ :deep(.v-tab:focus::before),
491
+ :deep(.v-tab:hover::before) {
492
+ background-color: transparent !important;
493
+ opacity: 0 !important;
494
+ }
495
+
496
+ :deep(.v-tabs__slider) {
497
+ height: 38px !important;
498
+ background: url(../../assets/img/tab-1.png) no-repeat;
499
+ background-size: 100% 100%;
500
+ }
501
+
502
+ .expBox {
503
+ :deep(td) {
504
+ border-bottom: thin solid rgba(123, 123, 123, 0.22);
505
+ }
506
+
507
+ :deep(.v-data-table__empty-wrapper td) {
508
+ border: none;
509
+ }
510
+
511
+ :deep(.v-checkbox.v-selection-control--dirty) {
512
+ background-color: rgba(33, 149, 243, 0.06) !important;
513
+
514
+ .v-label {
515
+ color: rgb(33, 150, 243) !important;
516
+ }
517
+
518
+ .active {
519
+ color: rgb(33, 150, 243);
520
+ display: inline-block !important;
521
+ }
522
+ }
523
+
524
+ :deep(.v-selection-control__input) {
525
+ display: none;
526
+ }
527
+
528
+ :deep(.v-checkbox) {
529
+ padding: 4px 8px 4px 10px;
530
+ border-radius: 6px;
531
+ background-color: rgba(117, 117, 117, 0.06);
532
+
533
+ .v-label {
534
+ margin-top: 3px;
535
+ font-size: 12px;
536
+ }
537
+ }
538
+ }
539
+ </style>
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <VRadioGroup v-bind="attrs" :modelValue="modelValue" @update:modelValue="onUpdate">
3
+ <VRadio
4
+ v-for="item in items"
5
+ :key="item.id || item.code"
6
+ :label="item.codeValue"
7
+ :value="item.code"
8
+ />
9
+ </VRadioGroup>
10
+ </template>
11
+
12
+ <script setup>
13
+ import { ref, onMounted, useAttrs } from 'vue';
14
+ import Request from '../../commons/request';
15
+
16
+ // 定义组件名称
17
+ defineOptions({
18
+ name: "VtkRadio",
19
+ inheritAttrs: false,
20
+ });
21
+ // 定义 props
22
+ const props = defineProps({
23
+ /**
24
+ * 字典来源:
25
+ * - 静态数据,如:"0:否/1:是"
26
+ * - 字典键名,如:"DISABLE_TYPE"
27
+ */
28
+ list: {
29
+ type: String,
30
+ required: true,
31
+ },
32
+ modelValue: {
33
+ type: [String, Number],
34
+ default: null,
35
+ },
36
+ });
37
+
38
+ // 定义 emit 事件
39
+ const emit = defineEmits(['update:modelValue']);
40
+
41
+ // 获取透传属性(替代 $attrs)
42
+ const attrs = useAttrs();
43
+
44
+ // 响应式数据
45
+ const items = ref([]);
46
+
47
+ // 处理静态数据
48
+ const statc = () => {
49
+ props.list.split('/').forEach(it => {
50
+ const [code, codeValue] = it.split(':');
51
+ items.value.push({ code, codeValue });
52
+ });
53
+ };
54
+
55
+ // 请求远程字典
56
+ const miscs = async () => {
57
+ try {
58
+ const res = await Request.getForm(`dict/misc/list/${props.list}`);
59
+ items.value = res.data || [];
60
+ } catch (error) {
61
+ console.error('[VtkRadio] Failed to load dictionary:', error);
62
+ items.value = [];
63
+ }
64
+ };
65
+
66
+ // 更新 v-model 值
67
+ const onUpdate = (val) => {
68
+ emit('update:modelValue', val);
69
+ };
70
+
71
+ // 初始化数据
72
+ onMounted(() => {
73
+ if (props.list.includes(':')) {
74
+ statc();
75
+ } else {
76
+ miscs();
77
+ }
78
+ });
79
+ </script>
80
+
81
+ <style scoped>
82
+ </style>