@yxhl/specter-pui-vtk 1.0.71 → 1.0.73

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yxhl/specter-pui-vtk",
3
- "version": "1.0.71",
3
+ "version": "1.0.73",
4
4
  "description": "雅心互联 Vue 3 + Vuetify3 组件库",
5
5
  "type": "module",
6
6
  "main": "./dist/specter-pui.umd.js",
@@ -1,73 +1,75 @@
1
- /**
2
- * 将数值转换成中文 '0:未启/1:已启'
3
- *
4
- * @param {*} val 数值
5
- * @param {*} map 值对
6
- * @return {string} 中文值
7
- */
8
- export function dict(val, map) {
9
- let value = "";
10
- if (val && val.includes(",")) {
11
- value = val.split(",").map(item => pre(item, map)).join();
12
- } else {
13
- value = pre(val, map);
14
- }
15
- return value;
16
- }
17
-
18
- function pre(value, map) {
19
- if (value) {
20
- var val = Array.isArray(value) ? value : value.split(",");
21
- var maps = map?.split("/");
22
- var results = [];
23
- for (var j = 0; j < val?.length; j++) {
24
- var currentVal = val[j];
25
- for (var i in maps) {
26
- var kv = maps[i].split(":");
27
- if (kv[0] == currentVal) {
28
- results.push(kv[1]);
29
- break;
30
- }
31
- }
32
- }
33
- return results.length > 0 ? results.join() : "其他";
34
- }
35
- }
36
-
37
- /**
38
- * 1,2字符残疾类别转换
39
- *
40
- * @param {*} val 数值
41
- * @return {string} 中文值
42
- */
43
- export function analyType(val) {
44
- if (!val) {
45
- return "";
46
- }
47
- let newVal = "";
48
- let arr = ["视力", "听力", "言语", "肢体", "智力", "精神", "多重"];
49
- for (let i = 1; i < 8; i++) {
50
- if (i === 1) {
51
- newVal = val.replace(i.toString(), arr[i - 1]);
52
- } else {
53
- newVal = newVal.replace(i.toString(), arr[i - 1]);
54
- }
55
- }
56
- return newVal;
57
- }
58
-
59
- export function analyLevel(val) {
60
- if (!val) {
61
- return "";
62
- }
63
- let newVal = "";
64
- let arr = ["一级", "二级", "三级", "四级", "不限等级"];
65
- for (let i = 1; i < 7; i++) {
66
- if (i === 1) {
67
- newVal = val.replace(i.toString(), arr[i - 1]);
68
- } else {
69
- newVal = newVal.replace(i.toString(), arr[i - 1]);
70
- }
71
- }
72
- return newVal;
73
- }
1
+ /**
2
+ * 将数值转换成中文 '0:未启/1:已启'
3
+ *
4
+ * @param {*} val 数值
5
+ * @param {*} map 值对
6
+ * @return {string} 中文值
7
+ */
8
+ export function dict(val, map) {
9
+ let value = "";
10
+ if (val && val.includes(",")) {
11
+ value = val.split(",").map(item => pre(item, map)).join();
12
+ } else {
13
+ value = pre(val, map)
14
+ }
15
+ return value;
16
+ }
17
+
18
+ function pre(value, map) {
19
+ if (value) {
20
+ var val = Array.isArray(value) ? value : value.split(",");
21
+ var maps = map?.split("/");
22
+ var results = []; // 创建一个空数组,用于存储匹配到的值
23
+ // 遍历 val 数组
24
+ for (var j = 0; j < val?.length; j++) {
25
+ var currentVal = val[j]; // 获取当前要查找的键
26
+ for (var i in maps) {
27
+ var kv = maps[i].split(":");
28
+ if (kv[0] == currentVal) {
29
+ results.push(kv[1]); // 将匹配到的值添加到结果数组中
30
+ break; // 找到匹配项后,跳出内层循环
31
+ }
32
+ }
33
+ }
34
+ return results.length > 0 ? results.join() : "其他";
35
+ }
36
+
37
+ }
38
+
39
+
40
+ /**
41
+ * 1,2字符残疾类别转换
42
+ *
43
+ * @param {*} val 数值
44
+ * @return {string} 中文值
45
+ */
46
+ export function analyType(val) {
47
+ if (!val) {
48
+ return ''
49
+ }
50
+ let newVal = ''
51
+ let arr = ['视力', '听力', '言语', '肢体', '智力', '精神', '多重']
52
+ for (let i = 1; i < 8; i++) {
53
+ if (i === 1) {
54
+ newVal = val.replace(i.toString(), arr[i - 1])
55
+ } else {
56
+ newVal = newVal.replace(i.toString(), arr[i - 1])
57
+ }
58
+ }
59
+ return newVal;
60
+ }
61
+ export function analyLevel(val) {
62
+ if (!val) {
63
+ return ''
64
+ }
65
+ let newVal = ''
66
+ let arr = ['一级', '二级', '三级', '四级', '不限等级']
67
+ for (let i = 1; i < 7; i++) {
68
+ if (i === 1) {
69
+ newVal = val.replace(i.toString(), arr[i - 1])
70
+ } else {
71
+ newVal = newVal.replace(i.toString(), arr[i - 1])
72
+ }
73
+ }
74
+ return newVal;
75
+ }
@@ -1,9 +1,12 @@
1
1
  import Request from "./request.js";
2
2
  import Storage from "./storage.js";
3
+ import Message from "../components/message/index.js";
3
4
 
4
5
  function isAid(value) {
5
6
  const str = String(value || "");
6
7
  return !!str && !/^(https?:)?\/\//i.test(str) && !str.includes("/") && !str.includes("?");
8
+
9
+
7
10
  }
8
11
 
9
12
  function getLinkFromResponse(data) {
@@ -109,6 +112,11 @@ export function GetQueryString(name, url = window.location.href) {
109
112
  * @return {Promise<string>} 拼接后的链接地址。
110
113
  */
111
114
  export async function appendAppParams(url, sobject = {}, sroute = "", stokenKey = "_mis_acis_token") {
115
+ if (url === undefined || url === null || url === "" || url === 0 || url === "0") {
116
+ Message.toast("应用标识错误", { color: "error" });
117
+ return "";
118
+ }
119
+
112
120
  const source = url || window.location.href;
113
121
 
114
122
  if (!source) {
@@ -3,7 +3,14 @@
3
3
  <div v-if="!inline" class="date-input" ref="inputRef" @click="togglePicker">
4
4
  <input type="text" :value="displayValue" :placeholder="placeholder" :disabled="disabled" readonly
5
5
  class="date-input-field" />
6
- <span class="date-icon">📅</span>
6
+ <span class="date-icon" aria-hidden="true">
7
+ <svg viewBox="0 0 24 24" fill="none" class="date-icon-svg">
8
+ <path
9
+ d="M7 2.75a.75.75 0 0 1 .75.75V5h8.5V3.5a.75.75 0 0 1 1.5 0V5h.75A2.5 2.5 0 0 1 21 7.5v10A2.5 2.5 0 0 1 18.5 20h-13A2.5 2.5 0 0 1 3 17.5v-10A2.5 2.5 0 0 1 5.5 5h.75V3.5A.75.75 0 0 1 7 2.75ZM4.5 10v7.5c0 .552.448 1 1 1h13c.552 0 1-.448 1-1V10h-15Zm1-3.5c-.552 0-1 .448-1 1v1h15v-1c0-.552-.448-1-1-1h-.75v1a.75.75 0 0 1-1.5 0v-1h-8.5v1a.75.75 0 0 1-1.5 0v-1H5.5Zm2.25 5.75a.75.75 0 0 1 .75.75v.25a.75.75 0 0 1-1.5 0V13a.75.75 0 0 1 .75-.75Zm4.25 0a.75.75 0 0 1 .75.75v.25a.75.75 0 0 1-1.5 0V13a.75.75 0 0 1 .75-.75Zm4.25 0a.75.75 0 0 1 .75.75v.25a.75.75 0 0 1-1.5 0V13a.75.75 0 0 1 .75-.75Zm-8.5 3.75a.75.75 0 0 1 .75.75V17a.75.75 0 0 1-1.5 0v-.25a.75.75 0 0 1 .75-.75Zm4.25 0a.75.75 0 0 1 .75.75V17a.75.75 0 0 1-1.5 0v-.25a.75.75 0 0 1 .75-.75Z"
10
+ fill="currentColor"
11
+ />
12
+ </svg>
13
+ </span>
7
14
  <span v-if="displayValue && !disabled" class="clear-icon" @click.stop="clearValue">✕</span>
8
15
  </div>
9
16
 
@@ -311,9 +318,13 @@ const dropdownStyle = ref({});
311
318
 
312
319
  // 当前日期(用于判断未来日期)
313
320
  const today = new Date();
314
- const todayYear = today.getFullYear();
315
- const todayMonth = today.getMonth();
316
- const todayDate = today.getDate();
321
+ const todayYear = today.getFullYear();
322
+ const todayMonth = today.getMonth();
323
+ const todayDate = today.getDate();
324
+
325
+ // 年份面板固定展示 12 个年份,并尽量让锚点年份落在中间偏前位置
326
+ const YEAR_PANEL_SIZE = 12;
327
+ const YEAR_PANEL_CENTER_OFFSET = 4;
317
328
 
318
329
  // 周和月份名称
319
330
  const weekDays = ['日', '一', '二', '三', '四', '五', '六'];
@@ -351,10 +362,11 @@ const formatDisplay = (value) => {
351
362
  };
352
363
 
353
364
  // 年份列表(显示12年)
354
- const yearList = computed(() => {
355
- const startYear = Math.floor(currentYear.value / 12) * 12;
356
- return Array.from({ length: 12 }, (_, i) => startYear + i);
357
- });
365
+ const yearList = computed(() => {
366
+ // 年份面板打开时,以当前锚点年份为中心生成 12 个年份选项
367
+ const startYear = currentYear.value - YEAR_PANEL_CENTER_OFFSET;
368
+ return Array.from({ length: YEAR_PANEL_SIZE }, (_, i) => startYear + i);
369
+ });
358
370
 
359
371
  // 周列表
360
372
  const weekList = computed(() => {
@@ -843,13 +855,18 @@ const changeViewMode = (mode) => {
843
855
  };
844
856
 
845
857
  // 面板操作
846
- const togglePicker = () => {
847
- if (props.disabled) return;
848
- showPicker.value = !showPicker.value;
849
- if (showPicker.value) {
850
- tempValue.value = selectedValue.value ?
851
- (Array.isArray(selectedValue.value) ? [...selectedValue.value] : selectedValue.value) :
852
- (props.mode === 'range' ? [] : null);
858
+ const togglePicker = () => {
859
+ if (props.disabled) return;
860
+ showPicker.value = !showPicker.value;
861
+ if (showPicker.value) {
862
+ // 年份单选打开面板时,优先围绕当前自然年展示年份列表
863
+ if (props.displayMode === 'year' && props.mode === 'single') {
864
+ currentYear.value = todayYear;
865
+ }
866
+
867
+ tempValue.value = selectedValue.value ?
868
+ (Array.isArray(selectedValue.value) ? [...selectedValue.value] : selectedValue.value) :
869
+ (props.mode === 'range' ? [] : null);
853
870
  viewMode.value = props.displayMode;
854
871
 
855
872
  // 初始化右侧日历/年份
@@ -999,7 +1016,15 @@ watch(() => props.modelValue, (newValue) => {
999
1016
  .date-icon {
1000
1017
  margin-left: 8px;
1001
1018
  color: rgb(var(--v-theme-on-surface), 0.6);
1002
- font-size: 14px;
1019
+ display: inline-flex;
1020
+ align-items: center;
1021
+ justify-content: center;
1022
+ }
1023
+
1024
+ .date-icon-svg {
1025
+ width: 16px;
1026
+ height: 16px;
1027
+ display: block;
1003
1028
  }
1004
1029
 
1005
1030
  .clear-icon {
@@ -1,164 +1,182 @@
1
- <template>
2
- <div class="search">
3
- <VMenu
4
- v-model="menu"
5
- @update:model-value="obtain"
6
- location="bottom end"
7
- origin="top end"
8
- offset="12"
9
- :close-on-content-click="false"
10
- >
11
- <template v-slot:activator="{ props }">
12
- <VTextField
13
- v-model="queryParam.condition"
14
- :placeholder="$attrs.placeholder || '请输入查询内容'"
15
- :style="$attrs.styles"
16
- class="box"
17
- density="compact"
18
- variant="outlined"
19
- hide-details
20
- @click:append="search"
21
- @keyup.enter="search"
22
- @click:clear="(queryParam.condition = ''), search()"
23
- clearable
24
- >
25
- <template v-slot:append-inner>
26
- <VBadge
27
- dot
28
- :content="initialIndex"
29
- :model-value="initialIndex > 0"
30
- color="error"
31
- class="advanced-badge"
32
- >
33
- <span
34
- v-bind="props"
35
- class="text-blue vtk-cursor-pointer d-inline-flex align-center text-body-2"
36
- >
37
- 高级
38
- </span>
39
- </VBadge>
40
- </template>
41
- </VTextField>
42
- </template>
43
-
44
- <VCard style="width: 380px;margin-right: -16px;overflow: visible;">
45
- <VCardTitle class="pr-3 d-flex justify-space-between align-center pb-5">
46
- <span>高级搜索</span>
47
- <VBtn class="mx-0" variant="text" icon @click="close" size="small">
48
- <VIcon>mdi-close</VIcon>
49
- </VBtn>
50
- </VCardTitle>
51
-
52
- <VCardText style="overflow: visible;">
53
- <slot></slot>
54
- </VCardText>
55
-
56
- <VCardActions class="pt-0 px-4 pb-3">
57
- <slot name="actions"></slot>
58
- <VSpacer />
59
- <VBtn @click="search" variant="flat" color="primary">
60
- <VIcon class="mr-1">mdi-magnify</VIcon>搜索
61
- </VBtn>
62
- <VBtn @click="reset" variant="flat" color="error">
63
- <VIcon class="mr-1">mdi-sync</VIcon>重置
64
- </VBtn>
65
- </VCardActions>
66
- </VCard>
67
- </VMenu>
68
- </div>
69
- </template>
70
-
71
- <script setup>
72
- import { ref, onMounted, watch } from 'vue';
73
-
74
- defineOptions({
75
- name: "VtkSearch"
76
- });
77
-
78
- const props = defineProps({
79
- value: {
80
- type: Object,
81
- default: () => ({})
82
- }
83
- });
84
-
85
- const emit = defineEmits(['update:value', 'search']);
86
-
87
- // 响应式数据
88
- const user = ref({});
89
- const menu = ref(false);
90
- const queryParam = ref({ ...props.value });
91
- const initialIndex = ref(0);
92
-
93
- // 监听 props.value 的变化
94
- watch(
95
- () => props.value,
96
- (newVal) => {
97
- queryParam.value = { ...newVal };
98
- },
99
- { deep: true }
100
- );
101
-
102
- onMounted(() => {
103
- user.value = JSON.parse(localStorage.getItem("_mis_acis_users") || "{}");
104
- initialIndex.value = 0;
105
- });
106
-
107
- // 方法
108
- const obtain = () => {
109
- if (!menu.value) {
110
- initialIndex.value = 0;
111
- }
112
- };
113
-
114
- const close = () => {
115
- menu.value = false;
116
- obtain();
117
- };
118
-
119
- const search = () => {
120
- close();
121
- emit('update:value', queryParam.value);
122
- emit("search");
123
- };
124
-
125
- // 重置
126
- const reset = () => {
127
- // 简化版重置逻辑
128
- Object.keys(queryParam.value).forEach(key => {
129
- if (key !== 'condition') {
130
- delete queryParam.value[key];
131
- }
132
- });
133
-
134
- delete queryParam.value.condition;
135
- initialIndex.value = 0;
136
- emit('update:value', queryParam.value);
137
- emit("search");
138
- menu.value = false;
139
- };
140
- </script>
141
-
142
- <style lang="scss" scoped>
143
- .search {
144
- position: relative;
145
- min-width: 380px;
146
- min-height: 40px;
147
- }
148
-
149
- .box {
150
- min-width: 380px;
151
- min-height: 40px;
152
- }
153
-
154
- :deep(.box .v-field__append-inner) {
155
- cursor: pointer;
156
- padding-left: 16px !important;
157
- padding-right: 4px !important;
158
- border-left: 1px #cccccc88 solid;
159
- min-width: 50px;
160
- display: flex;
161
- align-items: center;
162
- }
163
-
164
- </style>
1
+ <template>
2
+ <div class="search">
3
+ <VMenu
4
+ v-model="menu"
5
+ @update:model-value="obtain"
6
+ location="bottom end"
7
+ origin="top end"
8
+ offset="12"
9
+ :close-on-content-click="false"
10
+ >
11
+ <template v-slot:activator="{ props }">
12
+ <VTextField
13
+ v-model="queryParam.condition"
14
+ :placeholder="$attrs.placeholder || '请输入查询内容'"
15
+ :style="$attrs.styles"
16
+ class="box"
17
+ density="compact"
18
+ variant="outlined"
19
+ hide-details
20
+ @click:append="search"
21
+ @keyup.enter="search"
22
+ @click:clear="(queryParam.condition = ''), search()"
23
+ clearable
24
+ >
25
+ <template v-slot:append-inner>
26
+ <VBadge
27
+ dot
28
+ :content="initialIndex"
29
+ :model-value="initialIndex > 0"
30
+ color="error"
31
+ class="advanced-badge"
32
+ >
33
+ <span
34
+ v-bind="props"
35
+ class="text-blue vtk-cursor-pointer d-inline-flex align-center text-body-2"
36
+ >
37
+ 高级
38
+ </span>
39
+ </VBadge>
40
+ </template>
41
+ </VTextField>
42
+ </template>
43
+
44
+ <VCard style="width: 380px; margin-right: -16px; overflow: visible;">
45
+ <VCardTitle class="pr-3 d-flex justify-space-between align-center pb-5">
46
+ <span>高级搜索</span>
47
+ <VBtn class="mx-0" variant="text" icon @click="close" size="small">
48
+ <VIcon>mdi-close</VIcon>
49
+ </VBtn>
50
+ </VCardTitle>
51
+
52
+ <VCardText style="overflow: visible;">
53
+ <slot :query-param="queryParam"></slot>
54
+ </VCardText>
55
+
56
+ <VCardActions class="pt-0 px-4 pb-3">
57
+ <slot name="actions"></slot>
58
+ <VSpacer />
59
+ <VBtn @click="search" variant="flat" color="primary">
60
+ <VIcon class="mr-1">mdi-magnify</VIcon>搜索
61
+ </VBtn>
62
+ <VBtn @click="handleResetClick" variant="flat" color="error">
63
+ <VIcon class="mr-1">mdi-sync</VIcon>重置
64
+ </VBtn>
65
+ </VCardActions>
66
+ </VCard>
67
+ </VMenu>
68
+ </div>
69
+ </template>
70
+
71
+ <script setup>
72
+ import { computed, getCurrentInstance, onMounted, ref, watch } from 'vue';
73
+
74
+ defineOptions({
75
+ name: "VtkSearch"
76
+ });
77
+
78
+ const props = defineProps({
79
+ value: {
80
+ type: Object,
81
+ default: () => ({})
82
+ }
83
+ });
84
+
85
+ const emit = defineEmits(['update:value', 'search', 'reset']);
86
+ const instance = getCurrentInstance();
87
+
88
+ const user = ref({});
89
+ const menu = ref(false);
90
+ const queryParam = ref({ ...props.value });
91
+ const initialQueryParam = ref({ ...props.value });
92
+
93
+ const initialIndex = computed(() => {
94
+ return Object.entries(queryParam.value).filter(([key, val]) => {
95
+ if (key === 'condition') return false;
96
+ const initVal = initialQueryParam.value[key];
97
+ if (Array.isArray(val) && Array.isArray(initVal)) {
98
+ return JSON.stringify(val) !== JSON.stringify(initVal);
99
+ }
100
+ return val !== initVal;
101
+ }).length;
102
+ });
103
+
104
+ watch(
105
+ () => props.value,
106
+ (newVal) => {
107
+ queryParam.value = { ...newVal };
108
+ }
109
+ );
110
+
111
+ onMounted(() => {
112
+ user.value = JSON.parse(localStorage.getItem("_mis_acis_users") || "{}");
113
+ });
114
+
115
+ const obtain = () => {};
116
+
117
+ const close = () => {
118
+ menu.value = false;
119
+ obtain();
120
+ };
121
+
122
+ const filterNull = (obj) => {
123
+ return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== null && v !== undefined));
124
+ };
125
+
126
+ const search = () => {
127
+ close();
128
+ emit('update:value', filterNull(queryParam.value));
129
+ emit("search");
130
+ };
131
+
132
+ const reset = () => {
133
+ queryParam.value = {
134
+ ...initialQueryParam.value,
135
+ condition: ''
136
+ };
137
+ emit('update:value', filterNull(queryParam.value));
138
+ emit("search");
139
+ menu.value = false;
140
+ };
141
+
142
+ const handleResetClick = () => {
143
+ const hasParentResetHandler = Boolean(instance?.vnode.props?.onReset);
144
+
145
+ if (hasParentResetHandler) {
146
+ emit('reset', { ...queryParam.value });
147
+ return;
148
+ }
149
+
150
+ reset();
151
+ };
152
+
153
+ defineExpose({ search, reset });
154
+
155
+ </script>
156
+
157
+ <style lang="scss" scoped>
158
+ .search {
159
+ position: relative;
160
+ min-width: 380px;
161
+ min-height: 40px;
162
+ }
163
+
164
+ .box {
165
+ min-width: 380px;
166
+ min-height: 40px;
167
+ }
168
+
169
+ :deep(.box .v-field__append-inner) {
170
+ cursor: pointer;
171
+ padding-left: 16px !important;
172
+ padding-right: 4px !important;
173
+ border-left: 1px #cccccc88 solid;
174
+ min-width: 50px;
175
+ display: flex;
176
+ align-items: center;
177
+ }
178
+
179
+ :deep(.advanced-badge .v-badge__badge) {
180
+ transform: translate(10px, -2px);
181
+ }
182
+ </style>