sh-view 2.9.21 → 2.9.22

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 (29) hide show
  1. package/package.json +5 -5
  2. package/packages/components/sh-calendar/index.vue +1 -1
  3. package/packages/components/sh-date/index.vue +2 -2
  4. package/packages/components/sh-form/query.vue +1 -1
  5. package/packages/components/sh-progress/index.vue +7 -3
  6. package/packages/components/sh-table/components/importModal.vue +5 -5
  7. package/packages/components/sh-table/css/index.scss +1 -1
  8. package/packages/components/sh-table/js/useTable.js +4 -4
  9. package/packages/components/sh-table/table.vue +3 -0
  10. package/packages/components/sh-tabs/index.vue +9 -3
  11. package/packages/components/sh-toolbar/index.vue +1 -0
  12. package/packages/components/sh-tree/components/table-tree.vue +30 -27
  13. package/packages/components/sh-tree/index.vue +1 -1
  14. package/packages/components/sh-upload/index.vue +3 -3
  15. package/packages/css/main.scss +32 -0
  16. package/packages/mixin/index.js +7 -51
  17. package/packages/other/sh-preview/components/sh-excel.vue +1 -0
  18. package/packages/other/sh-preview/components/sh-image.vue +19 -0
  19. package/packages/other/sh-preview/index.vue +17 -8
  20. package/packages/vxeTable/css/index.scss +13 -9
  21. package/packages/vxeTable/index.js +29 -2
  22. package/packages/vxeTable/render/cell/vxe-render-goption.vue +2 -3
  23. package/packages/vxeTable/render/cell/vxe-render-input.vue +19 -2
  24. package/packages/vxeTable/render/cell/vxe-render-money.vue +11 -3
  25. package/packages/vxeTable/render/cell/vxe-render-progress.vue +1 -1
  26. package/packages/vxeTable/render/cell/vxe-render-textarea.vue +1 -1
  27. package/packages/vxeTable/render/cell/vxe-render-time.vue +1 -1
  28. package/packages/vxeTable/render/globalRenders.jsx +7 -7
  29. package/packages/vxeTable/render/mixin/cell-hooks.js +8 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-view",
3
- "version": "2.9.21",
3
+ "version": "2.9.22",
4
4
  "description": "基于vxe-table二次封装,更包含Alert,Badge,Card,CodeEditor,Col,Corner,CountTo,Drawer,Empty,Form,Header,Icon,List,Loading,Modal,Noticebar,Poptip,Progress,PullRefresh,Query,Result,Row,Split,Grid,Table,Tabs,Tag,Toolbar,Tree,Upload,WaterFall,WaterMark等丰富组件库",
5
5
  "main": "packages/index.js",
6
6
  "typings": "types/index.d.ts",
@@ -28,9 +28,9 @@
28
28
  "babel-polyfill": "^6.26.0",
29
29
  "codemirror": "^6.0.2",
30
30
  "core-js": "^3.32.2",
31
- "countup.js": "^2.8.0",
31
+ "countup.js": "^2.9.0",
32
32
  "cron-parser": "^4.8.1",
33
- "docx-preview": "^0.1.18",
33
+ "docx-preview": "^0.1.20",
34
34
  "exceljs": "^4.4.0",
35
35
  "jspdf": "^3.0.4",
36
36
  "jszip": "^3.10.1",
@@ -40,8 +40,8 @@
40
40
  "vue": "^3.5.20",
41
41
  "vue-masonry": "^0.16.0",
42
42
  "vue-router": "^4.5.1",
43
- "vxe-pc-ui": "^4.9.6",
44
- "vxe-table": "^4.16.1"
43
+ "vxe-pc-ui": "^4.11.24",
44
+ "vxe-table": "^4.17.36"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@typescript-eslint/eslint-plugin": "^6.9.0",
@@ -431,7 +431,7 @@ export default defineComponent({
431
431
  break
432
432
  }
433
433
  } catch (e) {
434
- proxy.msgwarning(e.message)
434
+ proxy.$vMessage.warning(e.message)
435
435
  }
436
436
  }
437
437
  const emitValue = (value, evnt) => {
@@ -21,7 +21,7 @@
21
21
  </vxe-pulldown>
22
22
  </template>
23
23
  <template v-else>
24
- <vxe-input
24
+ <vxe-date-picker
25
25
  v-model="inputValue"
26
26
  v-bind="inputConfig"
27
27
  @input="dispatch('input', $event)"
@@ -38,7 +38,7 @@
38
38
  @suffix-click="dispatch('suffix-click', $event)"
39
39
  @date-prev="dispatch('date-prev', $event)"
40
40
  @date-today="dispatch('date-today', $event)"
41
- @date-next="dispatch('date-next', $event)"></vxe-input>
41
+ @date-next="dispatch('date-next', $event)"></vxe-date-picker>
42
42
  </template>
43
43
  </div>
44
44
  </template>
@@ -33,7 +33,7 @@ import useForm from './js/useForm'
33
33
  export default defineComponent({
34
34
  name: 'ShQuery',
35
35
  props: props,
36
- emits: ['submit', 'reset', 'edit-closed', 'submit-invalid', 'collapse', 'prefix-click', 'suffix-click'],
36
+ emits: ['submit', 'reset', 'edit-closed', 'submit-invalid', 'collapse', 'prefix-click', 'suffix-click', 'collapsed'],
37
37
  setup(props, context) {
38
38
  const { proxy } = getCurrentInstance()
39
39
  const useFormHooks = useForm(props, context, proxy)
@@ -31,6 +31,10 @@ export default defineComponent({
31
31
  type: Number,
32
32
  default: 0
33
33
  },
34
+ successAuto: {
35
+ type: Boolean,
36
+ default: true
37
+ },
34
38
  status: {
35
39
  type: String,
36
40
  default: 'normal' // normal', 'active', 'wrong', 'success'
@@ -97,11 +101,11 @@ export default defineComponent({
97
101
  if (isDown) {
98
102
  currentStatus.value = 'normal'
99
103
  emit('status-change', 'normal')
100
- } else {
101
- if (parseInt(props.percent, 10) === 100) {
104
+ } else if (parseInt(props.percent, 10) === 100) {
105
+ if (props.successAuto) {
102
106
  currentStatus.value = 'success'
103
- emit('status-change', 'success')
104
107
  }
108
+ emit('status-change', 'success')
105
109
  }
106
110
  }
107
111
 
@@ -136,13 +136,13 @@ export default defineComponent({
136
136
  const handleImportDataBtn = async (type = 'all') => {
137
137
  let importData = type === 'all' ? importTableData.value : shtable.value.getSelectionData()
138
138
  if (!importData || !Array.isArray(importData) || importData.length < 1) {
139
- proxy.msginfo('未导入数据')
139
+ proxy.$vMessage.info('未导入数据')
140
140
  return false
141
141
  }
142
142
  if (props.needValidate) {
143
143
  let validateErrMap = await handleImportDataValidate(importData)
144
144
  if (validateErrMap) {
145
- proxy.msgerror('导入校验失败,请检查数据')
145
+ proxy.$vMessage.error('导入校验失败,请检查数据')
146
146
  return validateErrMap
147
147
  }
148
148
  }
@@ -180,7 +180,7 @@ export default defineComponent({
180
180
  if (typeof props.downloadTemplateFinished === 'function') {
181
181
  props.downloadTemplateFinished(options)
182
182
  }
183
- proxy.msgsuccess('下载模板完成!')
183
+ proxy.$vMessage.success('下载模板完成!')
184
184
  }
185
185
  // 选择导入文件按钮
186
186
  const handleImportFileBtn = async () => {
@@ -201,9 +201,9 @@ export default defineComponent({
201
201
  if (typeof props.importFileFinished === 'function') {
202
202
  props.importFileFinished(options)
203
203
  }
204
- proxy.msgsuccess('导入完成!')
204
+ proxy.$vMessage.success('导入完成!')
205
205
  } catch (e) {
206
- proxy.msgerror(e.message || e)
206
+ proxy.$vMessage.error(e.message || e)
207
207
  }
208
208
  }
209
209
  // 读取文件
@@ -36,7 +36,7 @@
36
36
  left: 0;
37
37
  width: 100%;
38
38
  height: 100%;
39
- padding: 0.5em 1em;
39
+ padding: 8px;
40
40
  background-color: var(--bg-layout-color);
41
41
  }
42
42
  .sh-table-footer{
@@ -150,7 +150,7 @@ export default function (props, context, proxy, isGrid) {
150
150
  return Object.assign({ seqMethod: obj => seqMethod(obj, pagerConfig) }, props.seqConfig)
151
151
  })
152
152
  const tableMoneyConfig = computed(() => {
153
- return Object.assign({ style: { width: '60px' } }, $vTableSetup.moneyConfig, props.moneyConfig, tableVmConfig.value)
153
+ return Object.assign({ style: { width: '70px' } }, $vTableSetup.moneyConfig, props.moneyConfig, tableVmConfig.value)
154
154
  })
155
155
  const tableExportConfig = computed(() =>
156
156
  Object.assign(
@@ -582,7 +582,7 @@ export default function (props, context, proxy, isGrid) {
582
582
  // 自定义操作列点击事件
583
583
  const handleGoptionClick = async (btnObj, dataObj) => {
584
584
  if (btnObj.code === 'delete' || btnObj.idConfirm) {
585
- await proxy.msgconfirm({ content: btnObj.ConfirmContent || `确定${btnObj.content}吗?` })
585
+ await proxy.$vMessage.confirm({ content: btnObj.ConfirmContent || `确定${btnObj.content}吗?` })
586
586
  }
587
587
  emit('globaloption', btnObj, dataObj)
588
588
  }
@@ -611,11 +611,11 @@ export default function (props, context, proxy, isGrid) {
611
611
  if (isTool) {
612
612
  let selectedRows = getSelectionData()
613
613
  if (selectedRows.length < 1) {
614
- proxy.msgwarning('请选择要删除的行!')
614
+ proxy.$vMessage.warning('请选择要删除的行!')
615
615
  return
616
616
  }
617
617
  deleteRows = selectedRows
618
- await proxy.msgconfirm({ content: `确定删除吗?` })
618
+ await proxy.$vMessage.confirm({ content: `确定删除吗?` })
619
619
  } else if (typeof props.onToolbarBtnDeleteBefore === 'function') {
620
620
  let result = await props.onToolbarBtnDeleteBefore(deleteRows)
621
621
  if (!result) return
@@ -165,6 +165,9 @@ export default defineComponent({
165
165
  slotRefs.forEach(slotRef => {
166
166
  otherHeight += slotRef.value?.offsetHeight || 0
167
167
  })
168
+ if (useTableHooks.zoomStatus.value) {
169
+ otherHeight += 16
170
+ }
168
171
  tableHeight.value = parentHeight - otherHeight
169
172
  }
170
173
 
@@ -11,9 +11,9 @@
11
11
  <template v-for="(tab, tabIndex) in tabList" :key="tabIndex">
12
12
  <div v-bind="getTabItemBind(tab, tabIndex)" @click="handleChange(tab)">
13
13
  <slot name="tabItem" v-bind="{ ...tab, isActive: tab[labelKey] === activeKey }">
14
- <div v-if="tab.icon" class="sh-tab-icon"><sh-icon :type="tab.icon"></sh-icon></div>
14
+ <div v-if="tab.icon" class="sh-tab-icon"><sh-icon :type="tab.icon" :size="iconSize"></sh-icon></div>
15
15
  <div v-if="tab[labelField]" class="sh-tab-label">{{ tab[labelField] }}</div>
16
- <div v-if="getTabIsClosable(tab)" class="sh-tab-close" @click.stop="handleClose(tab)"><sh-icon type="ios-close"></sh-icon></div>
16
+ <div v-if="getTabIsClosable(tab)" class="sh-tab-close" @click.stop="handleClose(tab)"><sh-icon type="ios-close" :size="iconSize"></sh-icon></div>
17
17
  </slot>
18
18
  </div>
19
19
  </template>
@@ -108,6 +108,10 @@ export default defineComponent({
108
108
  const isHorizontal = computed(() => ['left', 'right'].includes(props.placement))
109
109
  const hasContent = computed(() => props.isContent && activeKey.value && props.options.map(item => item[props.labelKey]).includes(activeKey.value))
110
110
  const vmSize = computed(() => props.size || $vUiSetup.size)
111
+ const iconSize = computed(() => {
112
+ if (['small', 'mini'].includes(vmSize.value)) return 12
113
+ return 14
114
+ })
111
115
  const tabClass = computed(() => {
112
116
  return {
113
117
  'sh-tabs-card': props.type === 'card',
@@ -269,6 +273,7 @@ export default defineComponent({
269
273
  activeKey,
270
274
  hasContent,
271
275
  contentStyle,
276
+ iconSize,
272
277
  scrollPrev,
273
278
  scrollNext,
274
279
  handleScroll,
@@ -287,7 +292,7 @@ export default defineComponent({
287
292
  width: 100%;
288
293
  position: relative;
289
294
  display: flex;
290
- line-height: 2.3em;
295
+ line-height: 2.4em;
291
296
  &-size {
292
297
  &-medium {
293
298
  font-size: 1.25em;
@@ -303,6 +308,7 @@ export default defineComponent({
303
308
  display: inline-flex;
304
309
  align-items: center;
305
310
  font-size: 1.4em;
311
+ padding: 0 0.1em;
306
312
  &:hover {
307
313
  background-color: rgba(0, 0, 0, 0.1);
308
314
  }
@@ -153,6 +153,7 @@ export default defineComponent({
153
153
 
154
154
  return {
155
155
  leftActive,
156
+ vmSize,
156
157
  toolBtns,
157
158
  leftBtns,
158
159
  rightBtns,
@@ -90,34 +90,36 @@ export default defineComponent({
90
90
  return Object.assign(resultConfig, tableProps.value)
91
91
  })
92
92
 
93
- const initSelection = () => {
94
- let { modelValue, multiple } = props
93
+ const initSelection = async isForce => {
94
+ let { modelValue, multiple, isSelect } = props
95
95
  let nodeKey = props.nodeKey || 'id'
96
- setTimeout(() => {
97
- if (!tableRef.value || !modelValue?.length) return
98
- let tableFullData = tableRef.value.getTableData().fullData
99
- let checkRows = []
100
- $vUtils.eachTree(tableFullData, item => {
101
- if (props.modelValue.includes(item[nodeKey])) {
102
- checkRows.push(item)
103
- }
104
- })
105
- // 找到第一个选中节点的所有父级,进行展开
106
- if (checkRows.length > 0) {
107
- let expendNode = checkRows[0]
108
- let checkParent = $vUtils.findTree(tableFullData, item => item[nodeKey] === expendNode[nodeKey])
109
- let parentNodes = checkParent.nodes.filter(item => item[nodeKey] !== expendNode[nodeKey])
110
- tableRef.value.setTreeExpand(parentNodes, true)
111
- if (multiple) {
112
- tableRef.value.setCheckboxRow(checkRows, true)
113
- } else {
114
- tableRef.value.setRadioRow(checkRows[0])
115
- }
96
+ if (!tableRef.value) return
97
+ if (!isForce && $vUtils.isEqual(modelValue, treeValue.value)) {
98
+ // 非强制更新,如果两次值相等,返回
99
+ return
100
+ }
101
+ let tableFullData = tableRef.value.getTableData().fullData
102
+ let checkRows = []
103
+ $vUtils.eachTree(tableFullData, item => {
104
+ if (props.modelValue.includes(item[nodeKey])) {
105
+ checkRows.push(item)
116
106
  }
117
- setTimeout(() => {
118
- tableRef.value.scrollToRow({ [nodeKey]: modelValue[0] })
119
- })
120
107
  })
108
+ // 找到第一个选中节点的所有父级,进行展开
109
+ if (checkRows.length > 0) {
110
+ let expendNode = checkRows[0]
111
+ let checkParent = $vUtils.findTree(tableFullData, item => item[nodeKey] === expendNode[nodeKey])
112
+ let parentNodes = checkParent.nodes.filter(item => item[nodeKey] !== expendNode[nodeKey])
113
+ await tableRef.value.setTreeExpand(parentNodes, true)
114
+ await tableRef.value.scrollToRow(checkRows[0])
115
+ if (multiple) {
116
+ await tableRef.value.setCheckboxRow(checkRows, true)
117
+ } else {
118
+ await tableRef.value.setRadioRow(checkRows[0])
119
+ }
120
+ } else {
121
+ await tableRef.value.clearAll()
122
+ }
121
123
  }
122
124
  // 单选框变化
123
125
  const onRadioChange = obj => {
@@ -198,13 +200,14 @@ export default defineComponent({
198
200
  })
199
201
  }
200
202
 
201
- const filterChangeDebounce = $vUtils.debounce(handleMyTableFilter, 500)
203
+ const initChangeDebounce = $vUtils.debounce(initSelection, 100)
204
+ const filterChangeDebounce = $vUtils.debounce(handleMyTableFilter, 300)
202
205
  const offetChangeDebounce = $vUtils.debounce(handleOffsetChange, tableConfigIn.value.resizeConfig?.refreshDelay || 100)
203
206
 
204
207
  watch(
205
208
  () => props.modelValue,
206
209
  () => {
207
- initSelection(true)
210
+ initChangeDebounce()
208
211
  }
209
212
  )
210
213
  watch(
@@ -194,7 +194,7 @@ export default defineComponent({
194
194
  // 初始化获取服务配置数据
195
195
  await getServerConfigDataSourse()
196
196
  if (tableTreeRef.value) {
197
- tableTreeRef.value.initSelection()
197
+ tableTreeRef.value.initSelection(true)
198
198
  }
199
199
  }
200
200
  // 树节点选择变换事件
@@ -367,18 +367,18 @@ export default defineComponent({
367
367
  const checked = fileFormat.value.some(item => item.toLocaleLowerCase() === _file_format)
368
368
  if (!checked) {
369
369
  let errorMsg = `格式不正确,请上传 ${fileFormat.value.join(' ')} 格式文件`
370
- props.onFormatError ? props.onFormatError(file, errorMsg, fileList.value) : proxy.msginfo(errorMsg)
370
+ props.onFormatError ? props.onFormatError(file, errorMsg, fileList.value) : proxy.$vMessage.info(errorMsg)
371
371
  return
372
372
  }
373
373
  }
374
374
  // check maxSize
375
375
  if (maxSize && file.size > maxSize * 1024) {
376
376
  let errorMsg = `文件大小不能超过 ${maxSize / 1024} M`
377
- props.onExceededSize ? props.onExceededSize(file, errorMsg, fileList.value) : proxy.msginfo(errorMsg)
377
+ props.onExceededSize ? props.onExceededSize(file, errorMsg, fileList.value) : proxy.$vMessage.info(errorMsg)
378
378
  return
379
379
  }
380
380
  if (!action) {
381
- proxy.msginfo('上传地址不能为空')
381
+ proxy.$vMessage.info('上传地址不能为空')
382
382
  return
383
383
  }
384
384
  return true
@@ -205,3 +205,35 @@ input[type="number"]{ -moz-appearance: textfield; }
205
205
  }
206
206
  }
207
207
 
208
+ // 全局通用提醒个性化样式
209
+ .sh-message-box{
210
+ &.type--confirm{
211
+ .vxe-modal--box{
212
+ }
213
+ }
214
+ &.status--success{
215
+ .vxe-modal--box{
216
+ background-color: #edfff3;
217
+ border-color: var(--success-color);
218
+ }
219
+ }
220
+ &.status--warning{
221
+ .vxe-modal--box{
222
+ background-color: #fff9e6;
223
+ border-color: var(--warning-color);
224
+ }
225
+ }
226
+ &.status--error{
227
+ .vxe-modal--box{
228
+ background-color: #ffefe6;
229
+ border-color: var(--danger-color);
230
+ }
231
+ }
232
+ &.status--info{
233
+ .vxe-modal--box{
234
+ background-color: #f0faff;
235
+ border-color: var(--info-color);
236
+ }
237
+ }
238
+ }
239
+
@@ -1,41 +1,5 @@
1
- const msgDefault = {
2
- title: '',
3
- className: 'globalMessageBox',
4
- draggable: false
5
- }
6
-
7
1
  const mixin = {
8
2
  methods: {
9
- // 全局msg提示方法
10
- msg(options) {
11
- return this.$vTable.modal.message(options)
12
- },
13
- msginfo(options) {
14
- let opts = Object.assign({}, msgDefault, typeof options === 'string' ? { content: options, status: 'info' } : options)
15
- return this.msg(opts)
16
- },
17
- msgsuccess(options) {
18
- let opts = Object.assign({}, msgDefault, typeof options === 'string' ? { content: options, status: 'success' } : options)
19
- return this.msg(opts)
20
- },
21
- msgwarning(options) {
22
- let opts = Object.assign({}, msgDefault, typeof options === 'string' ? { content: options, status: 'warning' } : options)
23
- return this.msg(opts)
24
- },
25
- msgerror(options) {
26
- let opts = Object.assign({}, msgDefault, typeof options === 'string' ? { content: options, status: 'error' } : options)
27
- return this.msg(opts)
28
- },
29
- msgconfirm(options) {
30
- return new Promise(async resolve => {
31
- let opts = Object.assign({}, typeof options === 'string' ? { content: options } : options)
32
- let type = await this.$vTable.modal.confirm({
33
- showHeader: Boolean(opts.title),
34
- ...opts
35
- })
36
- if (type === 'confirm') resolve(type)
37
- })
38
- },
39
3
  // 全局路由跳
40
4
  routerTo(route) {
41
5
  let { name, params, query } = {}
@@ -45,8 +9,11 @@ const mixin = {
45
9
  params = route.params
46
10
  query = route.query
47
11
  }
48
- if (name.indexOf('isTurnByHref_') > -1) {
49
- window.open(name.split('_')[1])
12
+ if (String(name).startsWith('http')) {
13
+ window.open(name)
14
+ return
15
+ } else if (String(name).startsWith('href:')) {
16
+ window.open(String(name).replace('href:', ''))
50
17
  return
51
18
  }
52
19
  try {
@@ -56,24 +23,13 @@ const mixin = {
56
23
  }
57
24
  },
58
25
  // 全局路由返回上一页
59
- routerback() {
60
- this.$router.back()
26
+ routerBack() {
27
+ return this.$router.back()
61
28
  },
62
29
  // 全局判断是否有子节点
63
30
  hasChildren(item, name = 'children') {
64
31
  return item && item[name] && Array.isArray(item[name]) && item[name].length > 0
65
32
  },
66
- // 全局格式化名称
67
- formatTitle(item) {
68
- let { title, __titleIsFunction__ } = item.meta || {}
69
- if (!title) return false
70
- if (this.$config.useI18n) {
71
- if (title.includes('{{') && title.includes('}}')) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => this.$t(_.trim())))
72
- else if (__titleIsFunction__) title = item.meta.title
73
- else title = this.$t(item.name)
74
- } else title = (item.meta && item.meta.title) || item.name
75
- return title
76
- },
77
33
  // 配置继承方法
78
34
  getExtendConfig(config = {}, name, module) {
79
35
  let moduleConfig = config[module] || config.default || {}
@@ -154,6 +154,7 @@ export default defineComponent({
154
154
  <style lang="scss">
155
155
  .sh-office-excel {
156
156
  position: relative;
157
+ height: 100%;
157
158
  .wolf-table-scrollbar {
158
159
  &.vertical {
159
160
  opacity: 1 !important;
@@ -0,0 +1,19 @@
1
+ <script>
2
+ import { defineComponent, computed, getCurrentInstance } from 'vue'
3
+ import dataProps from '../js/data-props'
4
+ export default defineComponent({
5
+ name: 'ShImage',
6
+ props: dataProps,
7
+ emits: ['rendered', 'error'],
8
+ setup(props, context) {
9
+ const { proxy } = getCurrentInstance()
10
+ return {}
11
+ }
12
+ })
13
+ </script>
14
+
15
+ <template>
16
+ <vxe-image ref="rootRef" :src="src" v-bind="options"></vxe-image>
17
+ </template>
18
+
19
+ <style lang="scss" scoped></style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div class="sh-preview">
3
- <component :is="componentName" v-bind="componentProps" :style="componentStyles" @rendered="onRendered" @error="onError" />
2
+ <div class="sh-preview" :style="styles">
3
+ <component :is="componentName" v-bind="componentProps" @rendered="onRendered" @error="onError" />
4
4
  </div>
5
5
  </template>
6
6
 
@@ -41,6 +41,8 @@ export default defineComponent({
41
41
  const componentName = computed(() => {
42
42
  if (!props.url || !previewType.value) {
43
43
  return 'div'
44
+ } else if (['jpg', 'jpeg', 'jpe', 'png', 'gif'].includes(previewType.value)) {
45
+ return defineAsyncComponent(() => import('./components/sh-image.vue'))
44
46
  } else if (['word', 'doc', 'docx'].includes(previewType.value)) {
45
47
  return defineAsyncComponent(() => import('./components/sh-word.vue'))
46
48
  } else if (['excel', 'xlsx'].includes(previewType.value)) {
@@ -51,19 +53,25 @@ export default defineComponent({
51
53
  const componentProps = computed(() => {
52
54
  let srcPrefix = props.base || ''
53
55
  let srcProps = {
54
- src: srcPrefix + props.url,
55
- frameborder: 0
56
+ src: srcPrefix + props.url
56
57
  }
57
58
  if (componentName.value === 'iframe') {
59
+ srcProps.frameborder = 0
60
+ srcProps.width = props.width
61
+ srcProps.height = props.height
58
62
  srcProps.src += '#scrollbars=0&toolbar=0&statusbar=0'
59
63
  }
60
64
  return srcProps
61
65
  })
62
- const componentStyles = computed(() => {
63
- return {
66
+ const styles = computed(() => {
67
+ let resultStyle = {
64
68
  width: props.width,
65
- height: props.height
69
+ maxWidth: '100%'
66
70
  }
71
+ if (componentName.value !== 'iframe') {
72
+ resultStyle.height = props.height
73
+ }
74
+ return resultStyle
67
75
  })
68
76
 
69
77
  const onRendered = data => {
@@ -74,9 +82,9 @@ export default defineComponent({
74
82
  }
75
83
 
76
84
  return {
85
+ styles,
77
86
  componentName,
78
87
  componentProps,
79
- componentStyles,
80
88
  onRendered,
81
89
  onError
82
90
  }
@@ -88,5 +96,6 @@ export default defineComponent({
88
96
  .sh-preview {
89
97
  position: relative;
90
98
  overflow: auto;
99
+ max-width: 100%;
91
100
  }
92
101
  </style>
@@ -94,6 +94,12 @@ button:focus, .vxe-button.type--button:not(.is--disabled):focus{
94
94
  .vxe-modal--wrapper{
95
95
  width: calc(100%);
96
96
  height: calc(100%);
97
+ .vxe-modal--box{
98
+ .vxe-modal--footer{
99
+ border-top: 1px solid var(--border-color);
100
+ padding: 0.6em;
101
+ }
102
+ }
97
103
  &.vxe-modal--preview{
98
104
  .vxe-modal--box{
99
105
  .vxe-modal--content{
@@ -102,20 +108,18 @@ button:focus, .vxe-button.type--button:not(.is--disabled):focus{
102
108
  }
103
109
  }
104
110
  &.type--modal {
105
- .vxe-modal--header{
106
- background-color: var(--primary-weak-color);
107
- }
108
- .vxe-modal--footer{
109
- border-top: 1px solid var(--border-color);
110
- padding-top: 0.5rem;
111
- padding-bottom: 0.5em;
111
+ .vxe-modal--box{
112
+ .vxe-modal--header{
113
+ background-color: var(--primary-weak-color);
114
+ }
112
115
  }
113
116
  }
114
117
  &.type--confirm {
115
118
  .vxe-modal--box{
116
- width: 320px;
119
+ width: 340px;
120
+ min-height: 130px;
121
+ max-width: 96%;
117
122
  .vxe-modal--body{
118
- padding: 10px;
119
123
  }
120
124
  }
121
125
  }
@@ -74,7 +74,8 @@ let uiOptions = {
74
74
  loading: {
75
75
  icon: 'vxe-icon-spinner roll',
76
76
  text: '加载中...'
77
- }
77
+ },
78
+ message: { title: '', className: 'sh-message-box', draggable: false }
78
79
  }
79
80
 
80
81
  let tableOptions = {
@@ -250,6 +251,30 @@ VXETable.renderer.mixin(publicRenders)
250
251
  VXETable.renderer.mixin(extraRenders)
251
252
  VXETable.renderer.mixin(filterRenders)
252
253
 
254
+ // 便捷化全局提示
255
+ const statusList = ['info', 'success', 'warning', 'error']
256
+ const vModal = VXEUI.modal
257
+ let vMessage = {
258
+ confirm: options => {
259
+ return new Promise(async resolve => {
260
+ let opts = Object.assign({}, uiOptions.message, typeof options === 'string' ? { content: options } : options)
261
+ let type = await vModal.confirm({ showHeader: Boolean(opts.title), ...opts })
262
+ if (type === 'confirm') resolve(type)
263
+ })
264
+ }
265
+ }
266
+ let vNotice = {}
267
+ statusList.forEach(status => {
268
+ vMessage[status] = options => {
269
+ let opts = Object.assign({ status }, uiOptions.message, typeof options === 'string' ? { content: options } : options)
270
+ return vModal.message(opts)
271
+ }
272
+ vNotice[status] = options => {
273
+ let opts = Object.assign({ status }, uiOptions.message, typeof options === 'string' ? { content: options } : options)
274
+ return vModal.notification(opts)
275
+ }
276
+ })
277
+
253
278
  const index = {
254
279
  install(Vue, { uiOption, tableOption, pdfOption, xlsxOption, menuOption, editorOption }) {
255
280
  let vuiOption = utils.merge(uiOptions, uiOption)
@@ -268,7 +293,9 @@ const index = {
268
293
  Vue.use(VXETable)
269
294
 
270
295
  Vue.config.globalProperties.$vUtils = utils
271
- Vue.config.globalProperties.$vModal = VXEUI.modal
296
+ Vue.config.globalProperties.$vModal = vModal
297
+ Vue.config.globalProperties.$vMessage = vMessage
298
+ Vue.config.globalProperties.$vNotice = vNotice
272
299
  Vue.config.globalProperties.$vUi = VXEUI
273
300
  Vue.config.globalProperties.$vTable = VXETable
274
301
  Vue.config.globalProperties.$vUiSetup = vuiOption
@@ -3,8 +3,7 @@
3
3
  <template v-for="renderBtn in renderBtns" :key="renderBtn.code">
4
4
  <vxe-button
5
5
  v-if="isBtnRender(renderBtn)"
6
- v-ripple
7
- :type="rprops.type"
6
+ :mode="rprops.type"
8
7
  :size="rsize"
9
8
  :icon="getBtnContent(renderBtn).icon"
10
9
  :status="getBtnContent(renderBtn).status"
@@ -47,7 +46,7 @@ export default defineComponent({
47
46
  const isBtnRender = renderBtn => {
48
47
  if ($vUtils.has(renderBtn, 'condition')) {
49
48
  let condition = $vUtils.get(renderBtn, 'condition')
50
- return $vUtils.calculate(condition, props.rdata)
49
+ return $vUtils.calculate(condition, useCell.rdata.value)
51
50
  }
52
51
  return true
53
52
  }
@@ -6,7 +6,15 @@
6
6
  <span v-else @click="vxeInputPrefixClick">{{ rprops.prefixText }}</span>
7
7
  </span>
8
8
  <span v-else-if="controlButton" class="control-btn before" @click="vxeControlClick(false)">-</span>
9
- <vxe-input v-model="renderValue" v-bind="rprops" :size="rsize" :immediate="false" @change="vxeInputChange" @input="vxeBlurCallback"></vxe-input>
9
+ <component
10
+ :is="inputComponent"
11
+ v-model="renderValue"
12
+ v-bind="rprops"
13
+ :size="rsize"
14
+ :immediate="false"
15
+ @change="vxeInputChange"
16
+ @blur="vxeBlurCallback"
17
+ @clear="vxeClearCallback"></component>
10
18
  <span v-if="rprops.suffixText && rform" class="suffix">
11
19
  <vxe-button v-if="suffixButton" v-bind="psButtonConfig" @click="vxeInputSuffixClick">{{ rprops.suffixText }}</vxe-button>
12
20
  <span v-else @click="vxeInputSuffixClick">{{ rprops.suffixText }}</span>
@@ -33,7 +41,14 @@ export default defineComponent({
33
41
  const { $vUtils } = proxy
34
42
  const useCell = cellHooks(props, context, proxy)
35
43
 
36
- const controlButton = computed(() => useCell.rprops.value.control && ['number', 'float', 'integer'].includes(useCell.rprops.value.type))
44
+ const isNumberInput = computed(() => ['number', 'float', 'integer'].includes(useCell.rprops.value.type))
45
+ const inputComponent = computed(() => {
46
+ if (isNumberInput.value) {
47
+ return 'vxe-number-input'
48
+ }
49
+ return 'vxe-input'
50
+ })
51
+ const controlButton = computed(() => useCell.rprops.value.control && isNumberInput.value)
37
52
 
38
53
  // 输入框数字加减控制
39
54
  const vxeControlClick = bol => {
@@ -45,6 +60,8 @@ export default defineComponent({
45
60
 
46
61
  return {
47
62
  ...useCell,
63
+ isNumberInput,
64
+ inputComponent,
48
65
  controlButton,
49
66
  vxeControlClick
50
67
  }
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <span class="vxe-render--inner" :class="{ 'form-render': rform, 'td-render': !rform, 'td-all': rprops.bill }">
3
3
  <template v-if="redit || isEditAll">
4
- <vxe-number-input v-model="renderText" v-bind="rprops" :size="rsize" :immediate="false" @change="vxeInputChange" @input="vxeMoneyCallback" />
4
+ <vxe-number-input v-model="renderText" v-bind="rprops" :size="rsize" :immediate="false" @change="vxeInputChange" @blur="vxeMoneyBlur" @clear="vxeMoneyClear" />
5
5
  </template>
6
6
  <template v-else-if="rprops.bill">
7
7
  <template v-for="(bil, bilindex) in billGroups" :key="bilindex">
@@ -34,15 +34,23 @@ export default defineComponent({
34
34
  return cellValue
35
35
  })
36
36
 
37
- const vxeMoneyCallback = async ({ value, $event }) => {
37
+ const vxeMoneyBlur = async ({ value, $event }) => {
38
38
  let cellValue = $vUtils.multiply(value, useCell.rprops.value.moneyUnit || 1)
39
39
  useCell.setRenderValue(cellValue)
40
40
  }
41
41
 
42
+ const vxeMoneyClear = async ({ value, $event }) => {
43
+ let cellValue = $vUtils.multiply(value, useCell.rprops.value.moneyUnit || 1)
44
+ setTimeout(() => {
45
+ useCell.setRenderValue(cellValue)
46
+ }, 50)
47
+ }
48
+
42
49
  return {
43
50
  ...useCell,
44
51
  cellFormatValue,
45
- vxeMoneyCallback
52
+ vxeMoneyBlur,
53
+ vxeMoneyClear
46
54
  }
47
55
  }
48
56
  })
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <template v-if="redit || isEditAll">
3
- <vxe-input v-model="renderValue" type="number" v-bind="rprops" :size="rsize" @change="vxeInputChange" @input="vxeBlurCallback" />
3
+ <vxe-input v-model="renderValue" type="number" v-bind="rprops" :size="rsize" :immediate="false" @change="vxeInputChange" @blur="vxeBlurCallback" @clear="vxeClearCallback" />
4
4
  </template>
5
5
  <template v-else>
6
6
  <sh-progress :percent="renderText" v-bind="rprops" />
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <template v-if="redit || isEditAll">
3
- <vxe-textarea v-model="renderValue" v-bind="rprops" :size="rsize" @change="vxeInputChange" @input="vxeBlurCallback" />
3
+ <vxe-textarea v-model="renderValue" v-bind="rprops" :size="rsize" :immediate="false" @change="vxeInputChange" @blur="vxeBlurCallback" @clear="vxeClearCallback" />
4
4
  </template>
5
5
  <template v-else>
6
6
  <span>{{ renderText }}</span>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <template v-if="redit || isEditAll">
3
- <sh-date v-model="renderValue" v-bind="rprops" :size="rsize" :disabled-method="vxeDisabledMethod" @input="vxeChangeCallBack"></sh-date>
3
+ <sh-date v-model="renderValue" v-bind="rprops" :size="rsize" :disabled-method="vxeDisabledMethod" @change="vxeChangeCallBack" @clear="vxeChangeCallBack"></sh-date>
4
4
  </template>
5
5
  <template v-else>
6
6
  <span v-html="renderText"></span>
@@ -34,7 +34,7 @@ import vxeFilterComplex from './filters/vxe-filter-complex.vue'
34
34
  // 全局渲染器
35
35
  const publicRenders = {
36
36
  $vInput: {
37
- autofocus: '.td-render .vxe-input--inner',
37
+ autofocus: '.vxe-input--inner, .vxe-number-input--input',
38
38
  autoselect: true,
39
39
  renderCell(renderOpts, params) {
40
40
  return [<vxeRenderInput rparams={params} roptions={renderOpts} />]
@@ -50,7 +50,7 @@ const publicRenders = {
50
50
  }
51
51
  },
52
52
  $vTextArea: {
53
- autofocus: '.td-render .vxe-textarea--inner',
53
+ autofocus: '.vxe-textarea--inner',
54
54
  autoselect: true,
55
55
  renderCell(renderOpts, params) {
56
56
  return [<vxeRenderTextarea rparams={params} roptions={renderOpts} />]
@@ -63,7 +63,7 @@ const publicRenders = {
63
63
  }
64
64
  },
65
65
  $vSelect: {
66
- autofocus: '.td-render .vxe-input--inner',
66
+ autofocus: '.vxe-input--inner',
67
67
  autoselect: true,
68
68
  renderCell(renderOpts, params) {
69
69
  return [<vxeRenderSelect rparams={params} roptions={renderOpts} />]
@@ -76,7 +76,7 @@ const publicRenders = {
76
76
  }
77
77
  },
78
78
  $vTree: {
79
- autofocus: '.td-render .vxe-input--inner',
79
+ autofocus: '.vxe-input--inner',
80
80
  autoselect: true,
81
81
  renderCell(renderOpts, params) {
82
82
  return [<vxeRenderTree rparams={params} roptions={renderOpts} />]
@@ -89,7 +89,7 @@ const publicRenders = {
89
89
  }
90
90
  },
91
91
  $vTime: {
92
- autofocus: '.td-render .vxe-input--inner',
92
+ autofocus: '.vxe-date-picker--inner',
93
93
  autoselect: true,
94
94
  renderCell(renderOpts, params) {
95
95
  return [<vxeRenderTime rparams={params} roptions={renderOpts} />]
@@ -102,7 +102,7 @@ const publicRenders = {
102
102
  }
103
103
  },
104
104
  $vProgress: {
105
- autofocus: '.td-render .vxe-input--inner',
105
+ autofocus: '.vxe-input--inner',
106
106
  autoselect: true,
107
107
  renderCell(renderOpts, params) {
108
108
  return [<vxeRenderProgress rparams={params} roptions={renderOpts} />]
@@ -130,7 +130,7 @@ const publicRenders = {
130
130
  }
131
131
  },
132
132
  $vMoney: {
133
- autofocus: '.td-render .vxe-input--inner',
133
+ autofocus: '.vxe-number-input--input',
134
134
  autoselect: true,
135
135
  renderCell(renderOpts, params) {
136
136
  return [<vxeRenderMoney rparams={params} roptions={renderOpts} />]
@@ -1,4 +1,4 @@
1
- import { computed, ref, watch } from 'vue'
1
+ import { computed, ref, watch, nextTick } from 'vue'
2
2
 
3
3
  export default function (props, context, proxy) {
4
4
  const { $vUtils, $vTableSetup } = proxy
@@ -73,6 +73,12 @@ export default function (props, context, proxy) {
73
73
  const vxeBlurCallback = async ({ value, $event }) => {
74
74
  setRenderValue(value)
75
75
  }
76
+ // 输入框失去焦点回调
77
+ const vxeClearCallback = async ({ value, $event }) => {
78
+ setTimeout(() => {
79
+ setRenderValue(value)
80
+ }, 50)
81
+ }
76
82
  // 回调赋值
77
83
  const setRenderValue = value => {
78
84
  let cellValue = !$vUtils.isNone(value) ? value : renderValue.value
@@ -147,6 +153,7 @@ export default function (props, context, proxy) {
147
153
  vxeRadioCallBack,
148
154
  vxeCheckCallBack,
149
155
  vxeBlurCallback,
156
+ vxeClearCallback,
150
157
  getBillClass,
151
158
  getBillValue,
152
159
  setRenderValue