【更新】前端依赖大升级,业务代码等多处优化调整,完全去掉以往this.xxx等语法。

pull/189/MERGE
小诺 2023-12-31 13:06:54 +08:00 committed by 俞宝山
parent a216d0ccff
commit 4f9de9fc9a
164 changed files with 3284 additions and 3242 deletions

View File

@ -19,70 +19,70 @@
"dependencies": {
"@amap/amap-jsapi-loader": "1.0.1",
"@ant-design/colors": "7.0.0",
"@ant-design/icons-vue": "6.1.0",
"@antv/g2plot": "2.4.28",
"@ant-design/icons-vue": "7.0.1",
"@antv/g2plot": "2.4.31",
"@chenfengyuan/vue-qrcode": "2.0.0",
"@highlightjs/vue-plugin": "2.1.0",
"@tinymce/tinymce-vue": "5.0.0",
"@vue-office/docx": "1.2.0",
"@vue-office/excel": "1.2.0",
"@vue-office/pdf": "1.2.0",
"@tinymce/tinymce-vue": "5.1.1",
"@vue-office/docx": "1.3.2",
"@vue-office/excel": "1.4.7",
"@vue-office/pdf": "1.5.5",
"ant-design-vue": "3.2.14",
"axios": "1.1.3",
"cropperjs": "1.5.12",
"dayjs": "1.11.7",
"echarts": "5.4.0",
"axios": "1.6.2",
"cropperjs": "1.6.1",
"dayjs": "1.11.10",
"echarts": "5.4.3",
"echarts-stat": "1.2.0",
"enquire.js": "2.1.6",
"event-source-polyfill": "1.0.31",
"fuse.js": "6.6.2",
"highlight.js": "11.6.0",
"hotkeys-js": "3.10.1",
"js-pinyin": "0.1.9",
"fuse.js": "7.0.0",
"highlight.js": "11.9.0",
"hotkeys-js": "3.12.2",
"js-pinyin": "0.2.5",
"lodash-es": "4.17.21",
"nprogress": "0.2.0",
"pinia": "2.0.33",
"qs": "6.11.1",
"pinia": "2.1.7",
"qs": "6.11.2",
"screenfull": "6.0.2",
"sm-crypto": "0.3.11",
"sm-crypto": "0.3.13",
"snowflake-id": "1.1.0",
"sortablejs": "1.15.0",
"tinymce": "6.2.0",
"vue": "3.2.44",
"vue-cropper": "1.0.5",
"sortablejs": "1.15.1",
"tinymce": "6.8.1",
"vue": "3.3.10",
"vue-cropper": "1.1.1",
"vue-demi": "0.13.11",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
"vue3-colorpicker": "2.0.4",
"vue-i18n": "9.8.0",
"vue-router": "4.2.5",
"vue3-colorpicker": "2.2.3",
"vue3-tree-org": "4.2.2",
"vuedraggable-es": "4.1.1"
},
"devDependencies": {
"@antfu/eslint-config": "0.29.4",
"@babel/eslint-parser": "7.19.1",
"@vitejs/plugin-legacy": "3.0.2",
"@vitejs/plugin-vue": "4.1.0",
"@vitejs/plugin-vue-jsx": "3.0.1",
"@vue/compiler-sfc": "3.2.47",
"@vitejs/plugin-legacy": "5.2.0",
"@vitejs/plugin-vue": "4.5.2",
"@vitejs/plugin-vue-jsx": "3.1.0",
"@vue/compiler-sfc": "3.3.10",
"@vue/eslint-config-standard": "8.0.1",
"antd-less-to-css-variable": "1.0.5",
"autoprefixer": "10.4.13",
"eslint": "8.26.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-prettier": "4.2.1",
"autoprefixer": "10.4.16",
"eslint": "8.55.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-prettier": "5.0.1",
"eslint-plugin-vue": "9.7.0",
"less": "4.1.3",
"postcss": "8.4.21",
"prettier": "2.8.7",
"rollup-plugin-visualizer": "5.8.3",
"tailwindcss": "3.2.7",
"typescript": "4.9.5",
"unplugin-auto-import": "0.15.2",
"unplugin-vue-components": "0.24.1",
"vite": "4.2.1",
"postcss": "8.4.32",
"prettier": "3.1.0",
"rollup-plugin-visualizer": "5.10.0",
"tailwindcss": "3.3.6",
"typescript": "5.3.3",
"unplugin-auto-import": "0.17.2",
"unplugin-vue-components": "0.26.0",
"vite": "5.0.6",
"vite-plugin-compression": "0.5.1",
"vite-plugin-vue-setup-extend": "0.4.0",
"vue-eslint-parser": "9.1.0"
"vue-eslint-parser": "9.3.2"
},
"browserslist": [
"> 1%",

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/auth/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/auth/` + url, ...arg)
/**
* 绘画
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/auth/third/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/auth/third/` + url, ...arg)
/**
* 三方登录
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/auth/third/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/auth/third/` + url, ...arg)
/**
* 三方用户
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/biz/dict/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/biz/dict/` + url, ...arg)
/**
* 字典
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/biz/org/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/biz/org/` + url, ...arg)
/**
* 机构
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/biz/position/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/biz/position/` + url, ...arg)
/**
* 岗位
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/biz/user/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/biz/user/` + url, ...arg)
/**
* 人员接口api
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/config/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/config/` + url, ...arg)
/**
* 配置
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/dict/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/dict/` + url, ...arg)
/**
* 字典
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/email/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/email/` + url, ...arg)
/**
* 邮件
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/file/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/file/` + url, ...arg)
/**
* 文件
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/job/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/job/` + url, ...arg)
/**
* 定时任务
*
@ -22,10 +22,6 @@ export default {
jobPage(data) {
return request('page', data, 'get')
},
// 获取定时任务列表
jobList(data) {
return request('list', data, 'get')
},
// 提交表单 edit为true时为编辑默认为新增
submitForm(data, edit = false) {
return request(edit ? 'edit' : 'add', data)

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/log/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/log/` + url, ...arg)
/**
* 日志
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/message/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/message/` + url, ...arg)
/**
* 站内信
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/monitor/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/monitor/` + url, ...arg)
/**
* 监控
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/dev/sms/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/dev/sms/` + url, ...arg)
/**
* 短信
*

View File

@ -1,6 +1,6 @@
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/gen/basic/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/gen/basic/` + url, ...arg)
export default {
// 获取代码生成基础分页

View File

@ -1,6 +1,6 @@
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/gen/config/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/gen/config/` + url, ...arg)
export default {
// 获取代码生成详情配置列表

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/mobile/button/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/mobile/button/` + url, ...arg)
/**
* 按钮
*

View File

@ -13,10 +13,6 @@ export default {
mobileMenuTree(data) {
return request('tree', data, 'get')
},
// 获取移动端菜单列表
mobileMenuList(data) {
return request('list', data, 'get')
},
// 提交移动端菜单表单 edit为true时为编辑默认为新增
mobileMenuSubmitForm(data, edit = false) {
return request(edit ? 'edit' : 'add', data)

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/mobile/module/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/mobile/module/` + url, ...arg)
/**
* 类别
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/index/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/index/` + url, ...arg)
/**
* 系统首页控制器
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/org/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/org/` + url, ...arg)
/**
* 机构
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/position/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/position/` + url, ...arg)
/**
* 职位
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/button/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/button/` + url, ...arg)
/**
* 按钮
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/field/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/field/` + url, ...arg)
/**
* 字段
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/menu/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/menu/` + url, ...arg)
/**
* 菜单
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/module/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/module/` + url, ...arg)
/**
* 模块
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/role/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/role/` + url, ...arg)
/**
* 角色
*
@ -22,10 +22,6 @@ export default {
rolePage(data) {
return request('page', data, 'get')
},
// 获取角色列表
roleList(data) {
return request('list', data, 'get')
},
// 提交表单 edit为true时为编辑默认为新增
submitForm(data, edit = false) {
return request(edit ? 'edit' : 'add', data)

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/user/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/user/` + url, ...arg)
/**
* 用户接口api
*

View File

@ -10,7 +10,7 @@
*/
import { baseRequest } from '@/utils/request'
const request = (url, ...arg) => baseRequest(`/sys/userCenter/${url}`, ...arg)
const request = (url, ...arg) => baseRequest(`/sys/userCenter/` + url, ...arg)
/**
* 用户个人控制器
*

View File

@ -0,0 +1,103 @@
export const getYear = () => {
let v = []
let y = new Date().getFullYear()
for (let i = 0; i < 11; i++) {
v.push(y + i)
}
return v
}
export const data = {
second: ['0', '5', '15', '20', '25', '30', '35', '40', '45', '50', '55', '59'],
minute: ['0', '5', '15', '20', '25', '30', '35', '40', '45', '50', '55', '59'],
hour: [
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23'
],
day: [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
'28',
'29',
'30',
'31'
],
month: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
week: [
{
value: '1',
label: '周日'
},
{
value: '2',
label: '周一'
},
{
value: '3',
label: '周二'
},
{
value: '4',
label: '周三'
},
{
value: '5',
label: '周四'
},
{
value: '6',
label: '周五'
},
{
value: '7',
label: '周六'
}
],
year: getYear()
}

View File

@ -405,18 +405,10 @@
</template>
<script setup name="XnCron">
import { data, getYear } from './data.js'
const activeKey = ref('1')
const type = ref('0')
const defaultValue = ref('')
const modalVisible = ref(false)
const getYear = () => {
let v = []
let y = new Date().getFullYear()
for (let i = 0; i < 11; i++) {
v.push(y + i)
}
return v
}
const dateValue = ref({
second: {
type: '0',
@ -504,101 +496,6 @@
appoint: []
}
})
const data = {
second: ['0', '5', '15', '20', '25', '30', '35', '40', '45', '50', '55', '59'],
minute: ['0', '5', '15', '20', '25', '30', '35', '40', '45', '50', '55', '59'],
hour: [
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23'
],
day: [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
'28',
'29',
'30',
'31'
],
month: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'],
week: [
{
value: '1',
label: '周日'
},
{
value: '2',
label: '周一'
},
{
value: '3',
label: '周二'
},
{
value: '4',
label: '周三'
},
{
value: '5',
label: '周四'
},
{
value: '6',
label: '周五'
},
{
value: '7',
label: '周六'
}
],
year: getYear()
}
const props = defineProps({
modelValue: {
@ -610,112 +507,63 @@
default: () => []
}
})
const setRules = (v) => {
switch (v.type) {
case '0':
return '*'
case '1':
return v.range.start + '-' + v.range.end
case '2':
return v.loop.start + '/' + v.loop.end
case '3':
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
case '4':
return 'L'
case '5':
return '?'
default:
return '*'
}
}
const value_second = computed(() => {
let v = dateValue.value.second
if (v.type === '0') {
return '*'
} else if (v.type === '1') {
return v.range.start + '-' + v.range.end
} else if (v.type === '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type === '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
return setRules(v)
})
const value_minute = computed(() => {
let v = dateValue.value.minute
if (v.type === '0') {
return '*'
} else if (v.type === '1') {
return v.range.start + '-' + v.range.end
} else if (v.type === '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type === '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
return setRules(v)
})
const value_hour = computed(() => {
let v = dateValue.value.hour
if (v.type === '0') {
return '*'
} else if (v.type === '1') {
return v.range.start + '-' + v.range.end
} else if (v.type === '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type === '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
return setRules(v)
})
const value_day = computed(() => {
let v = dateValue.value.day
if (v.type === '0') {
return '*'
} else if (v.type === '1') {
return v.range.start + '-' + v.range.end
} else if (v.type === '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type === '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else if (v.type === '4') {
return 'L'
} else if (v.type === '5') {
return '?'
} else {
return '*'
}
return setRules(v)
})
const value_month = computed(() => {
let v = dateValue.value.month
if (v.type === '0') {
return '*'
} else if (v.type === '1') {
return v.range.start + '-' + v.range.end
} else if (v.type === '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type === '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else {
return '*'
}
return setRules(v)
})
const value_week = computed(() => {
let v = dateValue.value.week
if (v.type === '0') {
return '*'
} else if (v.type === '1') {
return v.range.start + '-' + v.range.end
} else if (v.type === '2') {
return v.loop.end + '#' + v.loop.start
} else if (v.type === '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : '*'
} else if (v.type === '4') {
return v.last + 'L'
} else if (v.type === '5') {
return '?'
} else {
return '*'
}
return setRules(v)
})
const value_year = computed(() => {
let v = dateValue.value.year
if (v.type === '-1') {
return ''
} else if (v.type === '0') {
return '*'
} else if (v.type === '1') {
return v.range.start + '-' + v.range.end
} else if (v.type === '2') {
return v.loop.start + '/' + v.loop.end
} else if (v.type === '3') {
return v.appoint.length > 0 ? v.appoint.join(',') : ''
} else {
return ''
switch (v.type) {
case '-1':
return ''
case '0':
return '*'
case '1':
return v.range.start + '-' + v.range.end
case '2':
return v.loop.start + '/' + v.loop.end
case '3':
return v.appoint.length > 0 ? v.appoint.join(',') : ''
default:
return ''
}
})

View File

@ -2,7 +2,7 @@
<a-modal
:class="['my-modal', modalClass, simpleClass]"
:visible="visible"
v-bind="$props"
v-bind="props"
:width="modalWidth"
:wrap-class-name="wrapClassName + fullscreenClass"
@cancel="handleCancel"
@ -27,284 +27,259 @@
<slot name="insertFooter"></slot>
<slot name="footer">
<a-button @click="handleCancel">
{{ $props.cancelText || '取消' }}
{{ props.cancelText || '取消' }}
</a-button>
<slot name="centerFooter"></slot>
<a-button type="primary" @click="handleOk" :loading="loading">
{{ $props.okText || '确定' }}
{{ props.okText || '确定' }}
</a-button>
</slot>
<slot name="appendFooter"></slot>
</template>
</a-modal>
</template>
<script>
import props from './props.js'
export default {
name: 'DragModal',
mixins: [props],
props: {
//
modalClass: {
type: String,
default: 'modal-box'
},
//
wrapClassName: {
type: String,
default: ''
},
helpMessage: {
type: String
},
//
fullscreen: {
type: Boolean,
default: true
},
//
drag: {
type: Boolean,
default: true
},
//
resize: {
type: Boolean,
default: false
},
//
visible: {
type: Boolean,
default: false
},
//
title: {
type: String,
default: undefined
},
//
width: {
type: [Number, String],
default: '70%'
},
loading: {
type: Boolean,
default: undefined
}
<script setup>
import mixinProps from './props.js'
import { useSlots } from 'vue'
const slots = useSlots()
const props = defineProps({
...mixinProps,
//
modalClass: {
type: String,
default: 'modal-box'
},
emits: ['ok', 'close', 'fullscreen'],
data() {
return {
modalWidth: '',
contain: null,
//
header: null,
modalContent: null,
mouseDownX: 0,
mouseDownY: 0,
deltaX: 0,
deltaY: 0,
sumX: 0,
sumY: 0,
onmousedown: false,
//
modalBody: null,
myBody: null,
prevModalWidth: 0,
prevModalHeight: 0,
prevBodyWidth: 0,
prevBodyHeight: 0,
startX: 0,
startY: 0,
//
fullscreenClass: '',
fullscreenStatus: false
}
//
wrapClassName: {
type: String,
default: ''
},
computed: {
slotKeys() {
return Object.keys(this.$slots)
},
simpleClass() {
return Math.random().toString(36).substring(2)
}
helpMessage: {
type: String
},
watch: {
visible() {
this.$nextTick(() => {
this.initialEvent(this.visible)
})
},
fullscreenStatus() {
this.fullscreenClass = this.fullscreenStatus ? ' full-modal' : ''
}
//
fullscreen: {
type: Boolean,
default: true
},
mounted() {
this.$nextTick(() => {
this.initialEvent(this.visible)
//
drag: {
type: Boolean,
default: true
},
//
resize: {
type: Boolean,
default: false
},
//
visible: {
type: Boolean,
default: false
},
//
title: {
type: String,
default: undefined
},
//
width: {
type: [Number, String],
default: '70%'
},
loading: {
type: Boolean,
default: undefined
}
})
const emit = defineEmits(['ok', 'close', 'fullscreen'])
const modalWidth = ref('')
const contain = ref(null)
//
const header = ref(null)
const modalContent = ref(null)
const mouseDownX = ref(0)
const mouseDownY = ref(0)
const deltaX = ref(0)
const deltaY = ref(0)
const sumX = ref(0)
const sumY = ref(0)
const onmousedown = ref(false)
//
const modalBody = ref(null)
const myBody = ref(null)
const prevModalWidth = ref(0)
const prevModalHeight = ref(0)
const prevBodyWidth = ref(0)
const prevBodyHeight = ref(0)
const startX = ref(0)
const startY = ref(0)
//
const fullscreenClass = ref('')
const fullscreenStatus = ref(false)
const slotKeys = computed(() => {
return Object.keys(slots)
})
const simpleClass = computed(() => {
return Math.random().toString(36).substring(2)
})
onMounted(() => {
nextTick(() => {
initialEvent(props.visible)
})
})
watch(
() => props.visible,
(newValue) => {
nextTick(() => {
initialEvent(props.visible)
})
},
created() {},
beforeUnmount() {
this.removeMove()
document.removeEventListener('mouseup', this.removeUp, false)
this.removeResize()
document.removeEventListener('mouseup', this.removeResize)
},
methods: {
changeWidth(width) {
this.modalWidth = width
},
handleFullScreen(e) {
e?.stopPropagation()
e?.preventDefault()
}
)
watch(
() => fullscreenStatus.value,
(newValue) => {
fullscreenClass.value = fullscreenStatus.value ? ' full-modal' : ''
}
)
onBeforeUnmount(() => {
removeMove()
document.removeEventListener('mouseup', removeUp, false)
removeResize()
document.removeEventListener('mouseup', removeResize)
})
const changeWidth = (width) => {
modalWidth.value = width
}
const handleFullScreen = (e) => {
e?.stopPropagation()
e?.preventDefault()
this.fullscreenStatus = !this.fullscreenStatus
this.$emit('fullscreen', e)
},
handleOk(e) {
this.reset()
this.$emit('ok', e)
},
handleCancel(e) {
const classList = e.target?.classList
//
if (classList.contains('ant-modal-close-x') || classList.contains('ant-space-item')) {
return
fullscreenStatus.value = !fullscreenStatus.value
emit('fullscreen', e)
}
const handleOk = (e) => {
reset()
emit('ok', e)
}
const handleCancel = (e) => {
const classList = e.target?.classList
//
if (classList.contains('ant-modal-close-x') || classList.contains('ant-space-item')) {
return
}
reset()
emit('close', e)
}
const reset = () => {
//
mouseDownX.value = 0
mouseDownY.value = 0
deltaX.value = 0
deltaY.value = 0
sumX.value = 0
sumY.value = 0
//
prevModalWidth.value = 0
prevModalHeight.value = 0
prevBodyWidth.value = 0
prevBodyHeight.value = 0
startX.value = 0
startY.value = 0
//
fullscreenStatus.value = false
}
const initialEvent = (visible) => {
if (visible) {
reset()
//
document.removeEventListener('mouseup', removeUp, false)
contain.value = document.getElementsByClassName(simpleClass.value)[0]
changeWidth(props.width)
if (props.drag === true) {
header.value = contain.value.getElementsByClassName('ant-modal-header')[0]
modalContent.value = contain.value.getElementsByClassName('ant-modal-content')[0]
header.value.style.cursor = 'all-scroll'
modalContent.value.style.left = 0
modalContent.value.style.transform = 'translate(0px,0px)'
//
header.value.onmousedown = (event) => {
onmousedown.value = true
mouseDownX.value = event.pageX
mouseDownY.value = event.pageY
document.body.onselectstart = () => false
document.addEventListener('mousemove', handleMove, false)
}
this.reset()
this.$emit('close', e)
},
reset() {
//
this.mouseDownX = 0
this.mouseDownY = 0
this.deltaX = 0
this.deltaY = 0
this.sumX = 0
this.sumY = 0
//
this.prevModalWidth = 0
this.prevModalHeight = 0
this.prevBodyWidth = 0
this.prevBodyHeight = 0
this.startX = 0
this.startY = 0
//
this.fullscreenStatus = false
},
initialEvent(visible) {
// console.log('--------- ')
// console.log('simpleClass===>', this.simpleClass)
// console.log('document===>', document)
if (visible) {
this.reset()
//
document.removeEventListener('mouseup', this.removeUp, false)
this.contain = document.getElementsByClassName(this.simpleClass)[0]
// console.log('-contain:', this.contain)
this.changeWidth(this.$props.width)
if (this.$props.drag === true) {
this.header = this.contain.getElementsByClassName('ant-modal-header')[0]
this.modalContent = this.contain.getElementsByClassName('ant-modal-content')[0]
this.header.style.cursor = 'all-scroll'
this.modalContent.style.left = 0
this.modalContent.style.transform = 'translate(0px,0px)'
// console.log('-header:', this.header)
// console.log('-modalContent:', this.modalContent)
//
// this.contain.onmousedown = (event) => {
this.header.onmousedown = (event) => {
this.onmousedown = true
this.mouseDownX = event.pageX
this.mouseDownY = event.pageY
document.body.onselectstart = () => false
document.addEventListener('mousemove', this.handleMove, false)
}
document.addEventListener('mouseup', this.removeUp, false)
}
if (this.$props.resize === true) {
this.modalBody = this.contain.getElementsByClassName('ant-modal-content')[0]
this.myBody = this.contain.getElementsByClassName('ant-modal-body')[0]
this.modalBody.style.overflow = 'hidden'
this.modalBody.style.resize = 'both'
this.myBody.style.overflow = 'auto'
this.myBody.style.height = 'auto'
// console.log('-modalBody:', this.modalBody)
// console.log('-myBody:', this.myBody)
//
this.modalBody.onmousedown = (event) => {
event.preventDefault()
const rect = this.modalBody.getBoundingClientRect()
const rightBorder = rect.x + rect.width - 17
const bottomBorder = rect.y + rect.height - 17
// console.log('rightBorder:' + rightBorder, 'clientX:' + event.clientX)
// console.log('bottomBorder:' + bottomBorder, 'clientY:' + event.clientY)
if (event.clientX >= rightBorder && event.clientY >= bottomBorder) {
this.prevModalWidth = this.modalBody.offsetWidth
this.prevModalHeight = this.modalBody.offsetHeight
this.prevBodyWidth = this.myBody.offsetWidth
this.prevBodyHeight = this.myBody.offsetHeight
this.startX = event.clientX
this.startY = event.clientY
document.addEventListener('mousemove', this.handleResize)
}
document.addEventListener('mouseup', this.removeResize)
}
document.addEventListener('mouseup', removeUp, false)
}
if (props.resize === true) {
modalBody.value = contain.value.getElementsByClassName('ant-modal-content')[0]
myBody.value = contain.value.getElementsByClassName('ant-modal-body')[0]
modalBody.value.style.overflow = 'hidden'
modalBody.value.style.resize = 'both'
myBody.value.style.overflow = 'auto'
myBody.value.style.height = 'auto'
//
modalBody.value.onmousedown = (event) => {
event.preventDefault()
const rect = modalBody.value.getBoundingClientRect()
const rightBorder = rect.x + rect.width - 17
const bottomBorder = rect.y + rect.height - 17
if (event.clientX >= rightBorder && event.clientY >= bottomBorder) {
prevModalWidth.value = modalBody.value.offsetWidth
prevModalHeight.value = modalBody.value.offsetHeight
prevBodyWidth.value = myBody.value.offsetWidth
prevBodyHeight.value = myBody.value.offsetHeight
startX.value = event.clientX
startY.value = event.clientY
document.addEventListener('mousemove', handleResize)
}
document.addEventListener('mouseup', removeResize)
}
},
handleMove(event) {
if (this.fullscreenStatus) {
return
}
const delta1X = event.pageX - this.mouseDownX
const delta1Y = event.pageY - this.mouseDownY
this.deltaX = delta1X
this.deltaY = delta1Y
// console.log('delta1X:' + delta1X, 'sumX:' + this.sumX, 'delta1Y:' + delta1Y, 'sumY:' + this.sumY)
this.modalContent.style.transform = `translate(${delta1X + this.sumX}px, ${delta1Y + this.sumY}px)`
},
removeMove() {
document.removeEventListener('mousemove', this.handleMove, false)
},
removeUp(event) {
document.body.onselectstart = () => true
if (this.onmousedown && !(event.pageX === this.mouseDownX && event.pageY === this.mouseDownY)) {
this.onmousedown = false
this.sumX = this.sumX + this.deltaX
this.sumY = this.sumY + this.deltaY
// console.log('sumX:' + this.sumX, 'sumY:' + this.sumY)
}
this.removeMove()
// this.checkMove()
},
handleResize(event) {
if (this.fullscreenStatus) {
return
}
const diffX = event.clientX - this.startX
const diffY = event.clientY - this.startY
const minWidth = 180
const minHeight = 0
if (this.prevBodyWidth + diffX > minWidth) {
this.changeWidth(this.prevModalWidth + diffX + 'px')
// this.myBody.style.width = this.prevBodyWidth + diffX + 'px'
}
if (this.prevBodyHeight + diffY > minHeight) {
// this.modalBody.style.height = this.prevModalHeight + diffY + 'px'
this.myBody.style.height = this.prevBodyHeight + diffY + 'px'
}
},
removeResize() {
document.removeEventListener('mousemove', this.handleResize)
}
}
}
const handleMove = (event) => {
if (fullscreenStatus.value) {
return
}
const delta1X = event.pageX - mouseDownX.value
const delta1Y = event.pageY - mouseDownY.value
deltaX.value = delta1X
deltaY.value = delta1Y
modalContent.value.style.transform = `translate(${delta1X + sumX.value}px, ${delta1Y + sumY.value}px)`
}
const removeMove = () => {
document.removeEventListener('mousemove', handleMove, false)
}
const removeUp = (event) => {
document.body.onselectstart = () => true
if (onmousedown.value && !(event.pageX === mouseDownX.value && event.pageY === mouseDownY.value)) {
onmousedown.value = false
sumX.value = sumX.value + deltaX.value
sumY.value = sumY.value + deltaY.value
}
removeMove()
}
const handleResize = (event) => {
if (fullscreenStatus.value) {
return
}
const diffX = event.clientX - startX.value
const diffY = event.clientY - startY.value
const minWidth = 180
const minHeight = 0
if (prevBodyWidth.value + diffX > minWidth) {
changeWidth(prevModalWidth.value + diffX + 'px')
}
if (prevBodyHeight.value + diffY > minHeight) {
myBody.value.style.height = prevBodyHeight.value + diffY + 'px'
}
}
const removeResize = () => {
document.removeEventListener('mousemove', handleResize)
}
</script>
<style lang="less">
.ant-modal-close-x {

View File

@ -8,12 +8,7 @@
```javascript
import Ellipsis from '@/components/Ellipsis'
export default {
components: {
Ellipsis
}
}
// vue3 不需要利用compoents去注册组件引入后可直接使用
```

View File

@ -1,50 +1,48 @@
<script lang="jsx">
<template>
<ellipsis />
</template>
<script setup>
import { h } from 'vue'
import Tooltip from 'ant-design-vue/es/tooltip'
import { cutStrByFullLength, getStrFullLength } from './util'
import { useSlots } from 'vue'
const slots = useSlots()
export default {
name: 'Ellipsis',
components: {
Tooltip
const props = defineProps({
prefixCls: {
type: String,
default: 'ant-pro-ellipsis'
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-ellipsis'
},
tooltip: {
type: Boolean
},
length: {
type: Number,
required: true
},
lines: {
type: Number,
default: 1
},
fullWidthRecognition: {
type: Boolean,
default: false
}
tooltip: {
type: Boolean
},
methods: {
getStrDom(str, fullLength) {
return <span>{cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '')}</span>
},
getTooltip(fullStr, fullLength) {
return <Tooltip title={fullStr}>{this.getStrDom(fullStr, fullLength)}</Tooltip>
}
length: {
type: Number,
required: true
},
render() {
const { tooltip, length } = this.$props
const str = this.$slots
.default()
.map((vNode) => vNode.children)
.join('')
const fullLength = getStrFullLength(str)
const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength)
return strDom
lines: {
type: Number,
default: 1
},
fullWidthRecognition: {
type: Boolean,
default: false
}
})
const str = slots
.default()
.map((vNode) => vNode.children)
.join('')
const fullLength = getStrFullLength(str)
const showStr = cutStrByFullLength(str, props.length) + (fullLength > props.length ? '...' : '')
// 使h
const ellipsis = () => {
return props.tooltip && fullLength > props.length
? h(Tooltip, { title: str }, { default: () => showStr })
: // default: () => xxx
h('span', showStr)
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<div class="baiduMap" :style="{height: `${height}px`}">
<div class="baiduMap" :style="{ height: `${height}px` }">
<div :id="`container-${mid}`" style="width: 100%; height: 100%">地图资源加载中...</div>
</div>
</template>
@ -25,6 +25,7 @@
},
plugins: {
type: Array,
// eslint-disable-next-line vue/require-valid-default-prop
default: ['BMap.ScaleControl', 'BMap.ZoomControl', 'BMap.LocationControl', 'BMap.NavigationControl3D']
},
viewMode: {

View File

@ -1,5 +1,5 @@
<template>
<div class="gaodeMap" :style="{height: `${height}px`}">
<div class="gaodeMap" :style="{ height: `${height}px` }">
<div :id="`container-${mid}`" style="width: 100%; height: 100%">地图资源加载中...</div>
</div>
</template>
@ -26,6 +26,7 @@
},
plugins: {
type: Array,
// eslint-disable-next-line vue/require-valid-default-prop
default: ['AMap.ToolBar', 'AMap.Scale', 'AMap.HawkEye', 'AMap.MapType', 'AMap.Geolocation', 'AMap.MarkerCluster']
},
viewMode: {

View File

@ -36,70 +36,72 @@
</a-tabs>
</a-modal>
</template>
<script>
<script setup>
import config from '@/assets/icons/mobile'
export default {
data() {
return {
visible: false,
iconData: [],
modelValue: '',
activeKey: 'default',
iconItemDefault: 'default'
}
},
mounted() {
this.iconData.push(...config.icons)
},
methods: {
//
showIconModal(value) {
this.visible = true
this.defaultSetting(value)
},
//
defaultSetting(value) {
if (value) {
this.modelValue = value
//
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
this.activeKey = 'default'
if (value.indexOf('-two-tone') > -1) {
this.iconItemDefault = 'twotone'
} else if (value.indexOf('-filled') > -1) {
this.iconItemDefault = 'filled'
}
} else if (value.indexOf('-extend') > -1) {
//
this.activeKey = 'extend'
// ,
// this.iconItemDefault = 'json'
}
const visible = ref(false)
const iconData = ref([])
const modelValue = ref('')
const activeKey = ref('default')
const iconItemDefault = ref('default')
onMounted(() => {
iconData.value.push(...config.icons)
})
//
const showIconModal = (value) => {
visible.value = true
defaultSetting(value)
}
//
defineExpose({
showIconModal
})
//
const defaultSetting = (value) => {
if (value) {
modelValue.value = value
//
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
activeKey.value = 'default'
if (value.indexOf('-two-tone') > -1) {
iconItemDefault.value = 'twotone'
} else if (value.indexOf('-filled') > -1) {
iconItemDefault.value = 'filled'
}
},
//
paneChange(e) {
if (e.indexOf('default') === -1) {
this.iconItemDefault = 'default'
}
},
// icon
radioGroupChange(e) {
this.iconItemDefault = e.target.value
},
//
selectIcon(value) {
this.defaultValue = value
this.visible = false
// eslint-disable-next-line vue/require-explicit-emits
this.$emit('iconCallBack', this.defaultValue)
},
onCancel() {
this.visible = false
} else if (value.indexOf('-extend') > -1) {
//
activeKey.value = 'extend'
// ,
// this.iconItemDefault = 'json'
}
}
}
//
const paneChange = (e) => {
if (e.indexOf('default') === -1) {
iconItemDefault.value = 'default'
}
}
// icon
const radioGroupChange = (e) => {
iconItemDefault.value = e.target.value
}
const emit = defineEmits(['iconCallBack'])
//
const selectIcon = (value) => {
visible.value = false
// eslint-disable-next-line vue/require-explicit-emits
emit('iconCallBack', value)
}
const onCancel = () => {
visible.value = false
}
</script>
<style lang="less" scoped>

View File

@ -36,70 +36,73 @@
</a-tabs>
</a-modal>
</template>
<script>
<script setup>
import config from '@/config/iconSelect'
export default {
data() {
return {
visible: false,
iconData: [],
modelValue: '',
activeKey: 'default',
iconItemDefault: 'default'
}
},
mounted() {
this.iconData.push(...config.icons)
},
methods: {
//
showIconModal(value) {
this.visible = true
this.defaultSetting(value)
},
//
defaultSetting(value) {
if (value) {
this.modelValue = value
//
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
this.activeKey = 'default'
if (value.indexOf('-two-tone') > -1) {
this.iconItemDefault = 'twotone'
} else if (value.indexOf('-filled') > -1) {
this.iconItemDefault = 'filled'
}
} else if (value.indexOf('-extend') > -1) {
//
this.activeKey = 'extend'
// ,
// this.iconItemDefault = 'json'
}
const visible = ref(false)
const iconData = ref([])
const modelValue = ref('')
const activeKey = ref('default')
const iconItemDefault = ref('default')
onMounted(() => {
iconData.value.push(...config.icons)
})
//
const showIconModal = (value) => {
visible.value = true
defaultSetting(value)
}
//
defineExpose({
showIconModal
})
//
const defaultSetting = (value) => {
if (value) {
modelValue.value = value
//
if (value.indexOf('-outlined') > -1 || value.indexOf('-filled') > -1 || value.indexOf('-two-tone') > -1) {
activeKey.value = 'default'
if (value.indexOf('-two-tone') > -1) {
iconItemDefault.value = 'twotone'
} else if (value.indexOf('-filled') > -1) {
iconItemDefault.value = 'filled'
}
},
//
paneChange(e) {
if (e.indexOf('default') === -1) {
this.iconItemDefault = 'default'
}
},
// icon
radioGroupChange(e) {
this.iconItemDefault = e.target.value
},
//
selectIcon(value) {
this.defaultValue = value
this.visible = false
// eslint-disable-next-line vue/require-explicit-emits
this.$emit('iconCallBack', this.defaultValue)
},
onCancel() {
this.visible = false
} else if (value.indexOf('-extend') > -1) {
//
activeKey.value = 'extend'
// ,
// this.iconItemDefault = 'json'
}
}
}
//
const paneChange = (e) => {
if (e.indexOf('default') === -1) {
iconItemDefault.value = 'default'
}
}
// icon
const radioGroupChange = (e) => {
iconItemDefault.value = e.target.value
}
const emit = defineEmits(['iconCallBack'])
//
const selectIcon = (value) => {
visible.value = false
// eslint-disable-next-line vue/require-explicit-emits
emit('iconCallBack', value)
}
const onCancel = () => {
visible.value = false
}
</script>
<style lang="less" scoped>

View File

@ -13,29 +13,23 @@ iconSelector
```vue
<template>
<div>
<a-button type="primary" @click="$refs.iconselector.showModal(iconValue)">选择</a-button>
<icon-selector ref="iconselector" @callBack="iconCallBack"/>
<a-button type="primary" @click="openIcon(iconValue)">选择</a-button>
<icon-selector ref="iconselector" @iconCallBack="iconCallBack" />
</div>
</template>
<script>
import iconSelector from '@/components/Selector/iconSelector.vue'
<script setup>
const iconselector = ref() // 绑定ref="iconselector"
// 打开icon选择器
const openIcon = (iconValue) => {
iconselector.value.showIconModal(iconValue)
}
// 选择后回调
const iconCallBack = (value) => {
console.log('iconCallBack Icon', value)
}
export default {
name: 'YourView',
components: {
iconSelector
},
data () {
return {
}
},
methods: {
iconCallBack (icon) {
console.log('iconCallBack Icon', icon)
}
}
}
</script>
```

View File

@ -39,7 +39,7 @@
</div>
<div class="org-table">
<a-table
ref="table"
ref="tableRef"
size="small"
:columns="commons"
:data-source="tableData"
@ -143,7 +143,7 @@
}
]
// ref
const table = ref()
const tableRef = ref()
// ref
const selectedTable = ref()
const tableRecordNum = ref()

View File

@ -39,7 +39,7 @@
</div>
<div class="pos-table">
<a-table
ref="table"
ref="tableRef"
size="small"
:columns="commons"
:data-source="tableData"
@ -143,7 +143,7 @@
}
]
// ref
const table = ref()
const tableRef = ref()
// ref
const selectedTable = ref()
const tableRecordNum = ref()

View File

@ -39,7 +39,7 @@
</div>
<div class="role-table">
<a-table
ref="table"
ref="tableRef"
size="small"
:columns="commons"
:data-source="tableData"
@ -143,7 +143,7 @@
}
]
// ref
const table = ref()
const tableRef = ref()
// ref
const selectedTable = ref()
const tableRecordNum = ref()
@ -192,10 +192,13 @@
}
})
//
// eslint-disable-next-line vue/no-setup-props-destructure
const radioModel = props.radioModel
//
// eslint-disable-next-line vue/no-setup-props-destructure
const dataIsConverterFlw = props.dataIsConverterFlw
//
// eslint-disable-next-line vue/no-setup-props-destructure
const roleGlobal = props.roleGlobal
//
const current = ref(0) //

View File

@ -39,7 +39,7 @@
</div>
<div class="user-table">
<a-table
ref="table"
ref="tableRef"
size="small"
:columns="commons"
:data-source="tableData"
@ -143,7 +143,7 @@
}
]
// ref
const table = ref()
const tableRef = ref()
// ref
const selectedTable = ref()
const tableRecordNum = ref()

View File

@ -39,16 +39,10 @@
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10)
// eslint-disable-next-line no-unreachable
break
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
// eslint-disable-next-line no-unreachable
break
default:
return 0
// eslint-disable-next-line no-unreachable
break
}
}
</script>

View File

@ -21,7 +21,7 @@ Table 重封装组件说明
<template>
<s-table
ref="table"
ref="tableRef"
:rowKey="(record) => record.data.id"
:columns="columns"
:data="loadData"
@ -99,94 +99,80 @@ export default {
```vue
<template>
<s-table
ref="table"
:columns="columns"
:data="loadData"
>
<span slot="action" slot-scope="text, record">
<a>编辑</a>
<a-divider type="vertical"/>
<a-dropdown>
<a class="ant-dropdown-link">
更多 <a-icon type="down"/>
</a>
<a-menu slot="overlay">
<a-menu-item>
<a href="javascript:;">1st menu item</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">2nd menu item</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:;">3rd menu item</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
<s-table ref="tableRef" :columns="columns" :data="loadData" :alert="false" bordered :row-key="(record) => record.id">
<!-- #operator 插槽可以放入一些关于表格的操作,比如新增数据。 -->
<template #operator class="table-operator">
<a-space>
<a-button type="primary" @click="">
<template #icon><plus-outlined /></template>
新增
</a-button>
</a-space>
</template>
<!-- #bodyCell 放入column表格列需要显示的数据可以通过判断进行一个自定义显示 -->
<template #bodyCell="{ column, record }">
<template >
<a-avatar style="width: 25px; height: 25px" />
</template>
<template v-if="column.dataIndex === 'status'">
<!-- 进行自定义显示内容 -->
</template>
<!-- column.dataIndex === 'action' 时,可以进行编辑删除等关于这行数据的一个操作,操作内容可进行自定义 -->
<template v-if="column.dataIndex === 'action'">
<a @click="">编辑</a>
</template>
</template>
</s-table>
</template>
<script>
<script setup>
import STable from '@/components/table/'
export default {
components: {
STable,
const tableRef = ref() //一定要进行一个表格的ref绑定
const columns = ref([
{
title: '规则编号',
dataIndex: 'no',
},
data() {
return {
columns: [
{
title: '规则编号',
dataIndex: 'no',
},
{
title: '描述',
dataIndex: 'description',
},
{
title: '服务调用次数',
dataIndex: 'callNo',
},
{
title: '状态',
dataIndex: 'status',
},
{
title: '更新时间',
dataIndex: 'updatedAt',
},
{
table: '操作',
dataIndex: 'action',
scopedSlots: { customRender: 'action' },
},
],
// 查询条件参数
queryParam: {},
// 加载数据方法 必须为 Promise 对象
loadData: (parameter) => {
return this.$http.get('/service', {
params: Object.assign(parameter, this.queryParam),
}).then((res) => {
return res.result
})
},
}
{
title: '描述',
dataIndex: 'description',
},
methods: {
edit(row) {
// axios 发送数据到后端 修改数据成功后
// 调用 refresh() 重新加载列表数据
// 这里 setTimeout 模拟发起请求的网络延迟..
setTimeout(() => {
this.$refs.table.refresh() // refresh() 不传参默认值 false 不刷新到分页第一页
}, 1500)
},
{
title: '服务调用次数',
dataIndex: 'callNo',
},
{
title: '状态',
dataIndex: 'status',
},
{
title: '更新时间',
dataIndex: 'updatedAt',
},
{
table: '操作',
dataIndex: 'action',
scopedSlots: { customRender: 'action' },
},
])
const queryParam = ref({})
// 加载按钮数据
const loadData = (parameter) => {
return this.$http.get('/service', {
params: Object.assign(parameter, queryParam.value),
}).then((res) => {
return res.result
})
}
const edit = (row) => {
// axios 发送数据到后端 修改数据成功后
// 调用 refresh() 重新加载列表数据
// 这里 setTimeout 模拟发起请求的网络延迟..
setTimeout(() => {
tableRef.value.refresh() // refresh() 不传参默认值 false 不刷新到分页第一页
}, 1500)
}
</script>
```
@ -195,9 +181,9 @@ export default {
内置方法
----
通过 `this.$refs.table` 调用
通过 `声明的ref去调用 ==> tableRef.value` 调用
`this.$refs.table.refresh(true)` 刷新列表 (用户新增/修改数据后,重载列表数据)
`tableRef.value.refresh(true)` 刷新列表 (用户新增/修改数据后,重载列表数据)
> 注意:要调用 `refresh(bool)` 需要给表格组件设定 `ref`
>
@ -233,39 +219,83 @@ alert: {
>
> 文档中的结构有可能由于组件 bug 进行修正而改动。实际修改请以当时最新版本为准
修改 `@/components/table/index.js`156 行起
修改 `@/components/table/index.js`348 行起
```javascript
result.then(r => {
this.localPagination = this.showPagination && Object.assign({}, this.localPagination, {
current: r.pageNo, // 返回结果中的当前分页数
total: r.totalCount, // 返回结果中的总记录数
showSizeChanger: this.showSizeChanger,
pageSize: (pagination && pagination.pageSize) ||
this.localPagination.pageSize
}) || false
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
if (r.data.length === 0 && this.showPagination && this.localPagination.current > 1) {
this.localPagination.current--
this.loadData()
return
}
const data = reactive({
needTotalList: [],
localLoading: false,
localDataSource: [],
localPagination: Object.assign({}, props.pagination),
isFullscreen: false,
customSize: props.compSize,
columnsSetting: [],
localSettings: {
rowClassName: props.rowClassName,
rowClassNameSwitch: Boolean(props.rowClassName)
}
})
// 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
try {
if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) {
this.localPagination.hideOnSinglePage = true
}
} catch (e) {
this.localPagination = false
}
console.log('loadData -> this.localPagination', this.localPagination)
this.localDataSource = r.data // 返回结果中的数组数据
this.localLoading = false
})
// 这里的 data.xxx 是之前声明的
// 在 loadData() 方法中去获取后端数据,进行一个数据的加载更新
result.then((r) => {
if (r == null) {
data.localLoading = false
return
}
data.localPagination =
(props.showPagination &&
Object.assign({}, data.localPagination, {
current: r.current, // pageNo, // 返回结果中的当前分页数
total: r.total, // totalRows, // 返回结果中的总记录数
showSizeChanger: props.showSizeChanger,
pageSizeOptions: props.pageSizeOptions,
showTotal: (total, range) => {
return `${range[0]}-${range[1]} 共 ${total} 条 `
},
pageSize: (pagination && pagination.pageSize) || data.localPagination.pageSize
})) ||
false
// 后端数据records为null保存修复
if (r.records == null) {
r.records = []
}
// 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页
if (r.records.length === 0 && props.showPagination && data.localPagination.current > 1) {
data.localPagination.current--
loadData()
return
}
// 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能
try {
/*
if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.size))) {
data.localPagination.hideOnSinglePage = true
}
*/
if (!props.showPagination) {
data.localPagination.hideOnSinglePage = true
}
} catch (e) {
data.localPagination = false
}
// 返回结果中的数组数据
if (props.showPagination === false) {
// 既然配置了不分页,那么我们这里接收到肯定是数组
data.localDataSource = []
if (r instanceof Array) {
data.localDataSource = r
}
} else {
data.localDataSource = r.records
}
data.localLoading = false
getTableProps() // 获取到后端返回的数据后需要调用一下获取table的props的方法去刷新table
})
```
返回 JSON 例子:
```json
@ -336,4 +366,4 @@ result.then(r => {
更新时间
----
该文档最后更新于: 2019-06-23 PM 17:19
该文档最后更新于: 2023-12-27 PM 16:45

View File

@ -22,76 +22,70 @@
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable-es'
export default {
components: {
draggable
},
props: {
columns: {
type: Array,
default: () => []
}
},
data() {
return {
indeterminate: false,
checkAll: true,
columnsSetting: [],
originColumns: []
}
},
mounted() {
this.columnsSetting = this.columns.map((value) => {
if (value.checked == undefined) {
return {
...value,
checked: true
}
} else return value
})
// originColumns
this.originColumns = this.columnsSetting.map((value) => ({ ...value }))
//
const notCheckedList = this.columnsSetting.filter((value) => !value.checked)
if (notCheckedList.length) this.checkAll = false
},
methods: {
reset() {
this.columnsSetting = this.originColumns.map((value) => ({ ...value }))
this.indeterminate = false
const checkedList = this.columnsSetting.filter((value) => value.checked)
this.checkAll = checkedList.length === this.columnsSetting.length
this.emitColumnChange()
},
onChange() {
const checkedList = this.columnsSetting.filter((value) => value.checked)
this.indeterminate = Boolean(checkedList.length) && checkedList.length < this.columnsSetting.length
this.checkAll = checkedList.length === this.columnsSetting.length
this.emitColumnChange()
},
onCheckAllChange(e) {
e.preventDefault()
const val = e.target.checked
Object.assign(this, {
indeterminate: false,
checkAll: val,
columnsSetting: this.columns.map((value) => ({
...value,
checked: val
}))
})
this.emitColumnChange()
},
emitColumnChange() {
// eslint-disable-next-line vue/require-explicit-emits
this.$emit('columnChange', this.columnsSetting)
}
<script setup>
import Draggable from 'vuedraggable-es'
const emit = defineEmits(['columnChange'])
const props = defineProps({
columns: {
type: Array,
default: () => []
}
})
const indeterminate = ref(false)
const checkAll = ref(true)
const columnsSetting = ref([])
const originColumns = ref()
onMounted(() => {
columnsSetting.value = props.columns.map((value) => {
if (value.checked === undefined) {
return {
...value,
checked: true
}
} else return value
})
// originColumns
originColumns.value = columnsSetting.value.map((value) => ({ ...value }))
//
const notCheckedList = columnsSetting.value.filter((value) => !value.checked)
if (notCheckedList.length) checkAll.value = false
})
const reset = () => {
columnsSetting.value = originColumns.value.map((value) => ({ ...value }))
indeterminate.value = false
const checkedList = columnsSetting.value.filter((value) => value.checked)
checkAll.value = checkedList.length === columnsSetting.value.length
emitColumnChange()
}
const onChange = () => {
const checkedList = columnsSetting.value.filter((value) => value.checked)
indeterminate.value = Boolean(checkedList.length) && checkedList.length < columnsSetting.value.length
checkAll.value = checkedList.length === columnsSetting.value.length
emitColumnChange()
}
//
const onCheckAllChange = (e) => {
e.preventDefault()
const val = e.target.checked
indeterminate.value = false
checkAll.value = val
columnsSetting.value = props.columns.map((value) => ({
...value,
checked: val
}))
emitColumnChange()
}
const emitColumnChange = () => {
// eslint-disable-next-line vue/require-explicit-emits
emit('columnChange', columnsSetting.value)
}
</script>
<style lang="less" scoped></style>

View File

@ -1,36 +1,128 @@
<script lang="jsx">
<template>
<div className="table-wrapper">
<div className="s-table-tool">
<div className="s-table-tool-left">
<!-- 插槽操作按钮 -->
<slot name="operator"></slot>
</div>
<!-- 斑马纹 -->
<div className="layout-items-center s-table-tool-right">
<div className="layout-items-center ml-4" v-show="props.toolConfig.striped">
<a-checkbox :checked="data.localSettings.rowClassNameSwitch" @change="changeRowClass"> </a-checkbox>
</div>
<span v-for="item in tool">
<!-- 刷新 -->
<a-tooltip
v-if="item.name === 'refresh' && props.toolConfig.refresh"
:title="item.title"
class="s-tool-item"
@click="refresh"
>
<component class="icons" :is="item.icon"></component>
</a-tooltip>
<!-- 列展示 -->
<a-popover
v-if="item.isPopover && item.name === 'columnSetting' && props.toolConfig.columnSetting"
trigger="click"
placement="topLeft"
overlayClassName="s-table-column-settings"
arrow-point-at-center
>
<template #content>
<columnSetting :columns="props.columns" @columnChange="columnChange" />
</template>
<a-tooltip :title="item.title" class="s-tool-item">
<component class="icons" :is="item.icon"></component>
</a-tooltip>
</a-popover>
<!-- 密度 -->
<a-dropdown trigger="click" v-if="item.isDropdown && item.name == 'height' && props.toolConfig.height">
<template #overlay>
<a-menu selectable :selectedKeys="[data.customSize]" @click="changeHeight">
<a-menu-item key="default">默认</a-menu-item>
<a-menu-item key="middle">中等</a-menu-item>
<a-menu-item key="small">紧凑</a-menu-item>
</a-menu>
</template>
<a-tooltip :title="item.title" class="s-tool-item">
<component class="icons" :is="item.icon"></component>
</a-tooltip>
</a-dropdown>
</span>
</div>
</div>
<!-- 统计列数据 -->
<a-alert showIcon class="mb-4" v-if="props.alert">
<template #message>
<div>
<span className="mr-3">
已选择:{{ ' ' }}
<a className="font-6">
{{
props.rowSelection && props.rowSelection.selectedRowKeys ? props.rowSelection.selectedRowKeys.length : 0
}}
</a>
</span>
<span className="mr-3" v-for="item in data.needTotalList">
{{ item.title }} 总计{{ ' ' }}
<a className="font-6">{{ !item.customRender ? item.total : item.customRender(item.total) }}</a>
</span>
<a
v-show="
props.rowSelection && props.rowSelection.selectedRowKeys && props.rowSelection.selectedRowKeys.length > 0
"
className="ml-6"
@click="
rowClear(
typeof props.alert === 'boolean' && props.alert
? clearSelected()
: props.alert.clear && typeof props.alert.clear === 'function'
? props.alert.clear()
: null
)
"
>
{{ ' ' }}
清空{{ ' ' }}
</a>
</div>
</template>
</a-alert>
<!-- 表格 -->
<a-table
v-bind="{ ...renderTableProps, ...data.localSettings }"
@change="loadData"
:row-key="(record) => record.id"
@expand="
(expanded, record) => {
emit('expand', expanded, record)
}
"
>
<template #[item]="scope" v-for="item in renderSlots">
<slot :name="item" :scope="scope" v-bind="scope || {}"></slot>
</template>
</a-table>
</div>
</template>
<script setup>
import './index.less'
import { tableProps } from 'ant-design-vue/es/table/Table.js'
import { get } from 'lodash-es'
import draggable from 'vuedraggable-es'
import columnSetting from './columnSetting.vue'
import './index.less'
import i18n from '@/locales'
const { t } = i18n.global
import { useSlots } from 'vue'
import { useRoute } from 'vue-router'
const slots = useSlots()
const route = useRoute()
const emit = defineEmits(['expand'])
const renderSlots = Object.keys(slots)
export default {
name: 'STable',
components: {
draggable,
columnSetting
},
data() {
return {
needTotalList: [],
localLoading: false,
localDataSource: [],
localPagination: Object.assign({}, this.pagination),
isFullscreen: false,
customSize: this.compSize,
columnsSetting: [],
localSettings: {
rowClassName: this.rowClassName,
rowClassNameSwitch: Boolean(this.rowClassName)
}
}
},
// eslint-disable-next-line vue/order-in-components
props: Object.assign({}, tableProps(), {
const props = defineProps(
Object.assign({}, tableProps(), {
rowKey: {
type: [String, Function],
default: 'key'
@ -97,468 +189,356 @@
striped: false
})
}
}),
watch: {
pageNum(val) {
Object.assign(this.localPagination, {
current: val
})
},
size(val) {
Object.assign(this.localPagination, {
size: val
})
},
showSizeChanger(val) {
Object.assign(this.localPagination, {
showSizeChanger: val
})
},
columns(v) {
this.columnsSetting = v
}
},
created() {
const { current } = this.$route.params
const localPageNum = (current && parseInt(current)) || this.pageNum
this.localPagination =
(['auto', true].includes(this.showPagination) &&
Object.assign({}, this.localPagination, {
current: localPageNum,
pageSize: this.size, //this.compSize, size//
showSizeChanger: this.showSizeChanger,
defaultPageSize: this.defaultPageSize,
pageSizeOptions: this.pageSizeOptions,
showTotal: (total, range) => {
return `${range[0]}-${range[1]}${total}`
}
})) ||
false
this.needTotalList = this.initTotalList(this.columns)
this.loadData()
this.columnsSetting = this.columns
/*.map((c) => {
const tt = c.title
if (typeof tt === 'string') {
c.title = () => t(tt)
}
return c
})*/
},
methods: {
// true,
refresh(bool = false) {
bool &&
(this.localPagination = Object.assign(
{},
{
current: 1,
pageSize: this.localPagination.pageSize
}
))
this.loadData()
},
//
loadData(pagination, filters, sorter) {
this.localLoading = true
const parameter = Object.assign(
{
current:
(pagination && pagination.current) ||
(this.showPagination && this.localPagination.current) ||
this.pageNum,
// 使size
size:
(pagination && pagination.pageSize) ||
(this.showPagination && this.localPagination.pageSize) ||
this.pageSize ||
this.localPagination.pageSize
},
(sorter &&
sorter.field && {
sortField: sorter.field
}) ||
{},
(sorter &&
sorter.order && {
sortOrder: sorter.order
}) ||
{},
{
...filters
}
)
const result = this.data(parameter)
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then((r) => {
if (r == null) {
this.localLoading = false
return
}
this.localPagination =
(this.showPagination &&
Object.assign({}, this.localPagination, {
current: r.current, // pageNo, //
total: r.total, // totalRows, //
showSizeChanger: this.showSizeChanger,
pageSizeOptions: this.pageSizeOptions,
showTotal: (total, range) => {
return `${range[0]}-${range[1]}${total}`
},
pageSize: (pagination && pagination.pageSize) || this.localPagination.pageSize
})) ||
false
// recordsnull
if (r.records == null) {
r.records = []
}
// 0 ,
if (r.records.length === 0 && this.showPagination && this.localPagination.current > 1) {
this.localPagination.current--
this.loadData()
return
}
// table
try {
/*
if ((['auto', true].includes(this.showPagination) && r.total <= (r.pages * this.localPagination.size))) {
this.localPagination.hideOnSinglePage = true
}
*/
if (!this.showPagination) {
this.localPagination.hideOnSinglePage = true
}
} catch (e) {
this.localPagination = false
}
//
if (this.showPagination === false) {
//
this.localDataSource = []
if (r instanceof Array) {
this.localDataSource = r
}
} else {
this.localDataSource = r.records
}
this.localLoading = false
})
}
},
initTotalList(columns) {
const totalList = []
columns &&
columns instanceof Array &&
columns.forEach((column) => {
if (column.needTotal) {
totalList.push({
...column,
total: 0
})
}
})
return totalList
},
// total
updateSelect(selectedRowKeys, selectedRows) {
if (this.rowSelection) {
this.rowSelection.selectedRows = selectedRows
this.rowSelection.selectedRowKeys = selectedRowKeys
}
const list = this.needTotalList
this.needTotalList = list.map((item) => {
return {
...item,
total: selectedRows.reduce((sum, val) => {
const total = sum + parseInt(get(val, item.dataIndex))
return isNaN(total) ? 0 : total
}, 0)
}
})
},
// table
clearSelected() {
if (this.rowSelection) {
this.rowSelection.onChange([], [])
this.updateSelect([], [])
}
},
//
clearRefreshSelected(bool = false) {
this.refresh(bool)
this.clearSelected()
},
// table 使 clear
renderClear(callback) {
if (this.rowSelection && this.rowSelection.selectedRowKeys && this.rowSelection.selectedRowKeys.length > 0) {
return (
<a
className="ml-6"
onClick={() => {
callback()
this.clearSelected()
}}>
{' '}
清空{' '}
</a>
)
} else {
return null
}
},
renderAlert() {
//
const needTotalItems = this.needTotalList.map((item) => {
return (
<span className="mr-3">
{item.title} 总计{' '}
<a className="font-6">{!item.customRender ? item.total : item.customRender(item.total)}</a>
</span>
)
})
// alert
if (alert) {
const showAlert =
(typeof this.alert === 'object' &&
this.alert !== null &&
this.alert.show &&
typeof this.rowSelection.selectedRowKeys !== 'undefined') ||
this.alert
if (showAlert) {
//
const clearItem =
typeof this.alert === 'boolean' && this.alert
? this.renderClear(this.clearSelected)
: this.alert.clear && typeof this.alert.clear === 'function'
? this.renderClear(this.alert.clear)
: null
const message = (
<div>
<span className="mr-3">
已选择:{' '}
<a className="font-6">
{this.rowSelection && this.rowSelection.selectedRowKeys
? this.rowSelection.selectedRowKeys.length
: 0}
</a>
</span>
{needTotalItems}
{clearItem}
</div>
)
return <a-alert showIcon class="mb-4" message={message} />
}
}
},
columnChange(val) {
this.columnsSetting = val
},
renderHeader() {
let tools = [
{
name: 'refresh',
icon: <sync-outlined class="ml-4" />,
title: '刷新',
onClick: () => {
this.refresh()
}
},
{
name: 'height',
icon: <column-height-outlined />,
title: '密度',
isDropdown: true,
menu: () => {
const onClick = ({ key }) => {
this.customSize = key
}
return (
<a-menu onClick={onClick} selectable selectedKeys={[this.customSize]}>
<a-menu-item key="default">默认</a-menu-item>
<a-menu-item key="middle">中等</a-menu-item>
<a-menu-item key="small">紧凑</a-menu-item>
</a-menu>
)
},
onClick: () => {}
},
{
name: 'columnSetting',
icon: <setting-outlined />,
title: '列设置',
isPopover: true,
visible: false,
menu: () => {
return <columnSetting columns={this.columns} onColumnChange={this.columnChange} />
},
onClick: () => {}
}
]
if (this.extraTool.length) {
tools = tools.concat(this.extraTool)
}
})
)
//
const changeRowClass = (value) => {
const val = value.target.checked
this.localSettings.rowClassNameSwitch = val
const evenClass = val ? (_record, index) => (index % 2 === 1 ? 'table-striped' : null) : this.rowClassName
this.localSettings.rowClassName = evenClass
}
return (
<div className="s-table-tool">
<div className="s-table-tool-left">{this.$slots.operator && this.$slots.operator()}</div>
<div className="layout-items-center s-table-tool-right">
{this.toolConfig.striped ? (
<div className="layout-items-center ml-4">
<a-checkbox checked={this.localSettings.rowClassNameSwitch} onChange={changeRowClass}>
斑马纹
</a-checkbox>
</div>
) : null}
const data = reactive({
needTotalList: [],
localLoading: false,
localDataSource: [],
localPagination: Object.assign({}, props.pagination),
isFullscreen: false,
customSize: props.compSize,
columnsSetting: [],
localSettings: {
rowClassName: props.rowClassName,
rowClassNameSwitch: Boolean(props.rowClassName)
}
})
{tools.map((tool) => {
if (!this.toolConfig[tool.name]) {
return null
}
const tooltipEle = (
<a-tooltip title={tool.title} class="s-tool-item" onClick={tool.onClick}>
{tool.icon}
</a-tooltip>
)
if (tool.isPopover) {
return (
<a-popover
trigger={'click'}
placement="topLeft"
overlayClassName="s-table-column-settings"
arrow-point-at-center
content={tool.menu()}>
{tooltipEle}
</a-popover>
)
}
if (tool.isDropdown) {
return (
<a-dropdown trigger={['click']} overlay={tool.menu()}>
{tooltipEle}
</a-dropdown>
)
}
return tooltipEle
})}
</div>
</div>
)
}
},
render() {
let props = {}
const localKeys = Object.keys(this.$data)
Object.keys(tableProps()).forEach((k) => {
const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
if (localKeys.includes(localKey)) {
props[k] = this[localKey]
return props[k]
}
if (k === 'rowSelection') {
if (this.rowSelection) {
// 使alert rowSelection
props[k] = {
...this.rowSelection,
onChange: (selectedRowKeys, selectedRows) => {
this.updateSelect(selectedRowKeys, selectedRows)
typeof this[k].onChange !== 'undefined' && this[k].onChange(selectedRowKeys, selectedRows)
}
}
return props[k]
} else if (!this.rowSelection) {
// rowSelection
props[k] = null
return props[k]
}
}
if (k === 'customRow') {
if (this.lineSelection && this.rowSelection) {
// customRow
props[k] = (record, index) => {
return {
...(typeof this.customRow !== 'undefined' && this.customRow(record, index)),
onClick: (event) => {
// onClick
typeof this[k] !== 'undefined' &&
typeof this[k](record, index).onClick !== 'undefined' &&
this[k](record, index).onClick(event)
// disabled
const rowDisabled =
typeof this.rowSelection.getCheckboxProps !== 'undefined' &&
this.rowSelection.getCheckboxProps(record).disabled
if (rowDisabled) return
//
const classList = event.target?.classList
if (!classList.contains('ant-table-cell')) return
const key = (typeof this.rowKey === 'function' && this.rowKey(record)) || this.rowKey || index
let selectedRows = this.rowSelection.selectedRows
let selectedRowKeys = this.rowSelection.selectedRowKeys
const rowType = this.rowSelection?.type || 'checkbox'
if (rowType === 'radio' || this.rowSelection.selectedRowKeys === undefined) {
selectedRowKeys = [key]
selectedRows = [record]
} else if (!this.rowSelection.selectedRowKeys?.includes(key)) {
selectedRowKeys.push(key)
selectedRows.push(record)
} else {
const index = this.rowSelection.selectedRowKeys?.findIndex((itemKey) => itemKey === key)
selectedRows.splice(index, 1)
selectedRowKeys.splice(index, 1)
}
this.updateSelect(selectedRowKeys, selectedRows)
typeof this.rowSelection.onChange !== 'undefined' &&
this.rowSelection.onChange(selectedRowKeys, selectedRows)
}
}
}
return props[k]
}
}
this[k] && (props[k] = this[k])
//
props = {
...props,
size: this.customSize, // sizea-tablecompSize
columns: this.columnsSetting.filter((value) => value.checked === undefined || value.checked)
}
// 使scroll使
if (!props.scroll) {
props.scroll = { x: 1000 }
//
if (props.columns && props.columns.length > 10) {
props.scroll = { x: 1200 }
}
}
return props[k]
watch(
() => props.pageNum,
(newVal) => {
Object.assign(data.localPagination, {
current: newVal
})
const table = (
<a-table
{...props}
{...this.localSettings}
v-slots={this.$slots}
onChange={this.loadData}
onExpand={(expanded, record) => {
this.$emit('expand', expanded, record)
}}
/>
)
}
)
watch(
() => props.size,
(newVal) => {
Object.assign(data.localPagination, {
size: newVal
})
}
)
watch(
() => props.showSizeChanger,
(newVal) => {
Object.assign(data.localPagination, {
showSizeChanger: newVal
})
}
)
watch(
() => props.columns,
(newVal) => {
data.columnsSetting = newVal
}
)
return (
<div className="table-wrapper">
{this.renderHeader()}
{this.renderAlert()}
{table}
</div>
)
// props
const renderTableProps = ref([])
//
const tool = [
{
name: 'refresh',
icon: 'sync-outlined',
title: '刷新'
},
{
name: 'height',
icon: 'column-height-outlined',
title: '密度',
isDropdown: true
},
{
name: 'columnSetting',
icon: 'setting-outlined',
title: '列设置',
isPopover: true,
visible: false
}
]
//
const refresh = (bool = false) => {
bool &&
(data.localPagination = Object.assign(
{},
{
current: 1,
pageSize: data.localPagination.pageSize
}
))
loadData()
getTableProps()
}
//
const changeRowClass = (value) => {
const val = value.target.checked
data.localSettings.rowClassNameSwitch = val
const evenClass = val ? (_record, index) => (index % 2 === 1 ? 'table-striped' : null) : props.rowClassName
data.localSettings.rowClassName = evenClass
}
//
const changeHeight = (v) => {
data.customSize = v.key
getTableProps()
}
//
const columnChange = (val) => {
data.columnsSetting = val
getTableProps()
}
//
const rowClear = (callback) => {
callback
clearSelected()
}
//
const init = () => {
const { current } = route.params
const localPageNum = (current && parseInt(current)) || props.pageNum
data.localPagination =
(['auto', true].includes(props.showPagination) &&
Object.assign({}, data.localPagination, {
current: localPageNum,
pageSize: props.size, //props.compSize, size//
showSizeChanger: props.showSizeChanger,
defaultPageSize: props.defaultPageSize,
pageSizeOptions: props.pageSizeOptions,
showTotal: (total, range) => {
return `${range[0]}-${range[1]}${total}`
}
})) ||
false
data.needTotalList = initTotalList(props.columns)
data.columnsSetting = props.columns
loadData()
}
const initTotalList = (columns) => {
const totalList = []
columns &&
columns instanceof Array &&
columns.forEach((column) => {
if (column.needTotal) {
totalList.push({
...column,
total: 0
})
}
})
return totalList
}
//
const loadData = (pagination, filters, sorter) => {
data.localLoading = true
const parameter = Object.assign(
{
current:
(pagination && pagination.current) || (props.showPagination && data.localPagination.current) || props.pageNum,
// 使size
size:
(pagination && pagination.pageSize) ||
(props.showPagination && data.localPagination.pageSize) ||
props.pageSize ||
data.localPagination.pageSize
},
(sorter &&
sorter.field && {
sortField: sorter.field
}) ||
{},
(sorter &&
sorter.order && {
sortOrder: sorter.order
}) ||
{},
{
...filters
}
)
const result = props.data(parameter)
if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') {
result.then((r) => {
if (r == null) {
data.localLoading = false
return
}
data.localPagination =
(props.showPagination &&
Object.assign({}, data.localPagination, {
current: r.current, // pageNo, //
total: r.total, // totalRows, //
showSizeChanger: props.showSizeChanger,
pageSizeOptions: props.pageSizeOptions,
showTotal: (total, range) => {
return `${range[0]}-${range[1]}${total}`
},
pageSize: (pagination && pagination.pageSize) || data.localPagination.pageSize
})) ||
false
// recordsnull
if (r.records == null) {
r.records = []
}
// 0 ,
if (r.records.length === 0 && props.showPagination && data.localPagination.current > 1) {
data.localPagination.current--
loadData()
return
}
// table
try {
/*
if ((['auto', true].includes(props.showPagination) && r.total <= (r.pages * data.localPagination.size))) {
data.localPagination.hideOnSinglePage = true
}
*/
if (!props.showPagination) {
data.localPagination.hideOnSinglePage = true
}
} catch (e) {
data.localPagination = false
}
//
if (props.showPagination === false) {
//
data.localDataSource = []
if (r instanceof Array) {
data.localDataSource = r
}
} else {
data.localDataSource = r.records
}
data.localLoading = false
getTableProps()
})
}
}
// props
const getTableProps = () => {
let renderProps = {}
const localKeys = Object.keys(data)
Object.keys(tableProps()).forEach((k) => {
const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}`
if (localKeys.includes(localKey)) {
renderProps[k] = data[localKey]
return renderProps[k]
}
if (k === 'rowSelection') {
if (props.rowSelection) {
// 使alert rowSelection
renderProps[k] = {
...props.rowSelection,
onChange: (selectedRowKeys, selectedRows) => {
updateSelect(selectedRowKeys, selectedRows)
typeof props[k].onChange !== 'undefined' && props[k].onChange(selectedRowKeys, selectedRows)
}
}
return renderProps[k]
} else if (!props.rowSelection) {
// rowSelection
renderProps[k] = null
return renderProps[k]
}
}
if (k === 'customRow') {
if (props.lineSelection && props.rowSelection) {
// customRow
renderProps[k] = (record, index) => {
return {
...(typeof props.customRow !== 'undefined' && props.customRow(record, index)),
onClick: (event) => {
// onClick
typeof data[k] !== 'undefined' &&
typeof data[k](record, index).onClick !== 'undefined' &&
data[k](record, index).onClick(event)
// disabled
const rowDisabled =
typeof props.rowSelection.getCheckboxProps !== 'undefined' &&
props.rowSelection.getCheckboxProps(record).disabled
if (rowDisabled) return
//
const classList = event.target?.classList
if (!classList.contains('ant-table-cell')) return
const key = (typeof props.rowKey === 'function' && props.rowKey(record)) || props.rowKey || index
let selectedRows = props.rowSelection.selectedRows
let selectedRowKeys = props.rowSelection.selectedRowKeys
const rowType = props.rowSelection?.type || 'checkbox'
if (rowType === 'radio' || props.rowSelection.selectedRowKeys === undefined) {
selectedRowKeys = [key]
selectedRows = [record]
} else if (!props.rowSelection.selectedRowKeys?.includes(key)) {
selectedRowKeys.push(key)
selectedRows.push(record)
} else {
const index = props.rowSelection.selectedRowKeys?.findIndex((itemKey) => itemKey === key)
selectedRows.splice(index, 1)
selectedRowKeys.splice(index, 1)
}
updateSelect(selectedRowKeys, selectedRows)
}
}
}
return renderProps[k]
}
}
data[k] && (renderProps[k] = data[k])
//
renderProps = {
...renderProps,
bordered: props.bordered,
size: data.customSize, // sizea-tablecompSize
columns: data.columnsSetting.filter((value) => value.checked === undefined || value.checked)
}
return renderProps[k]
})
renderTableProps.value = renderProps
}
// total
const updateSelect = (selectedRowKeys, selectedRows) => {
if (props.rowSelection) {
// eslint-disable-next-line vue/no-mutating-props
props.rowSelection.selectedRows = selectedRows
// eslint-disable-next-line vue/no-mutating-props
props.rowSelection.selectedRowKeys = selectedRowKeys
getTableProps()
}
const list = data.needTotalList
data.needTotalList = list.map((item) => {
return {
...item,
total: selectedRows.reduce((sum, val) => {
const total = sum + parseInt(get(val, item.dataIndex))
return isNaN(total) ? 0 : total
}, 0)
}
})
}
// table
const clearSelected = () => {
if (props.rowSelection) {
props.rowSelection.onChange([], [])
updateSelect([], [])
getTableProps()
}
}
//
const clearRefreshSelected = (bool = false) => {
refresh(bool)
clearSelected()
}
//
defineExpose({
clearRefreshSelected,
refresh
})
onMounted(() => {
init()
})
</script>

View File

@ -8,7 +8,7 @@
<a-tag color="orange">{{ item.title }}</a-tag>
</template>
<template #actions>
<template v-for="{ key, label, icon, color } in props.actions">
<template v-for="{ key, label, icon, color } in props.actions" :key="key">
<a-tooltip :title="label">
<component :is="icon" @click="doAction(key, item)" :style="{ color: color }" />
</a-tooltip>
@ -22,8 +22,10 @@
<span class="xn-card-meta-title">{{ item.subTitle }}</span>
</template>
<template #description>
<div v-if="item.contents" v-for="content in item.contents">
<span>{{ content.label }}{{ content.value }}</span>
<div v-if="item.contents">
<div v-for="content in item.contents" :key="content.value">
<span>{{ content.label }}{{ content.value }}</span>
</div>
</div>
</template>
</a-card-meta>

View File

@ -3,86 +3,80 @@
<slot></slot>
</div>
</template>
<script setup>
const props = defineProps({
target: null,
show: Boolean
})
<script>
export default {
name: 'XnContextMenu',
props: {
target: null,
show: Boolean
},
data() {
return {
triggerShowFn: () => {},
triggerHideFn: () => {},
x: null,
y: null,
style: {},
binded: false
}
},
watch: {
show(show) {
if (show) {
this.bindHideEvents()
} else {
this.unbindHideEvents()
}
},
target(target) {
this.bindEvents()
}
},
mounted() {
this.bindEvents()
},
methods: {
//
bindEvents() {
this.$nextTick(() => {
if (!this.target || this.binded) return
this.triggerShowFn = this.contextMenuHandler.bind(this)
this.target.addEventListener('contextmenu', this.triggerShowFn)
this.binded = true
})
},
//
unbindEvents() {
if (!this.target) return
this.target.removeEventListener('contextmenu', this.triggerShowFn)
},
//
bindHideEvents() {
this.triggerHideFn = this.clickDocumentHandler.bind(this)
document.addEventListener('mousedown', this.triggerHideFn)
document.addEventListener('mousewheel', this.triggerHideFn)
},
//
unbindHideEvents() {
document.removeEventListener('mousedown', this.triggerHideFn)
document.removeEventListener('mousewheel', this.triggerHideFn)
},
//
clickDocumentHandler(e) {
this.$emit('update:show', false)
},
//
contextMenuHandler(e) {
this.x = e.clientX
this.y = e.clientY
this.layout()
this.$emit('update:show', true)
this.$emit('get-context-menu', e)
e.preventDefault()
},
//
layout() {
this.style = {
left: this.x + 'px',
top: this.y + 'px',
display: 'block'
}
}
const x = ref(null)
const y = ref(null)
const style = ref({})
const binded = ref(false)
const emit = defineEmits(['update:show', 'get-context-menu'])
// show
watch(
() => props.show,
(newValue) => {
newValue ? bindHideEvents() : unbindHideEvents()
}
)
watch(
() => props.target,
(newValue) => {
bindEvents()
}
)
//
const bindEvents = (e) => {
nextTick(() => {
if (!props.target || binded.value) return
props.target.addEventListener('contextmenu', contextMenuHandler)
binded.value = true
})
}
//
const bindHideEvents = () => {
document.addEventListener('mousedown', clickDocumentHandler)
document.addEventListener('mousewheel', clickDocumentHandler)
}
//
const unbindHideEvents = () => {
document.removeEventListener('mousedown', clickDocumentHandler)
document.removeEventListener('mousewheel', clickDocumentHandler)
}
//
const clickDocumentHandler = () => {
emit('update:show', false)
}
//
const contextMenuHandler = (e) => {
x.value = e.clientX
y.value = e.clientY
layout()
emit('update:show', true)
emit('get-context-menu', e)
e.preventDefault()
}
//
const layout = () => {
style.value = {
left: x.value + 'px',
top: y.value + 'px',
display: 'block'
}
}
//
const unbindEvents = () => {
if (!props.target) return
props.target.removeEventListener('contextmenu', contextMenuHandler)
}
</script>

View File

@ -4,44 +4,50 @@
<slot :name="slotKey" />
</template>
</a-modal>
<a-drawer v-else :visible="visible" v-bind="$attrs" :footer-style="{ textAlign: 'right' }">
<a-drawer v-else :visible="visible" v-bind="$attrs" @close="cancel" :footer-style="{ textAlign: 'right' }">
<template v-for="slotKey in slotKeys" #[slotKey]>
<slot :name="slotKey" />
</template>
</a-drawer>
</template>
<script>
import { mapState } from 'pinia'
<script setup>
import { useSlots } from 'vue'
const slots = useSlots()
import { globalStore } from '@/store'
const store = globalStore()
const props = defineProps({
visible: {
type: Boolean,
default: false,
required: true
}
})
const FormContainerTypeEnum = {
DRAWER: 'drawer',
MODAL: 'modal'
}
export default {
name: 'XnFormContainer',
inheritAttrs: false,
props: {
visible: {
type: Boolean,
default: false,
required: true
}
},
computed: {
...mapState(globalStore, ['formStyle']),
slotKeys() {
return Object.keys(this.$slots)
},
isModal() {
return FormContainerTypeEnum.MODAL === this.formStyle
}
},
methods: {
cancel() {
this.$emit('close')
}
}
const formStyle = computed(() => {
return store.formStyle
})
const slotKeys = computed(() => {
return Object.keys(slots)
})
const isModal = computed(() => {
return FormContainerTypeEnum.MODAL === formStyle.value
})
const emit = defineEmits(['close'])
const cancel = () => {
emit('close')
}
</script>
<script>
//
export default {
inheritAttrs: false
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<!-- 本组件这兄弟写的很好 请参照https://blog.csdn.net/weixin_41897680/article/details/124925222-->
<div class="hljs-container" :codetype="props.language">
<highlightjs :language="props.language" :autodetect="!props.language" :code="props.code"></highlightjs>
<highlightjs :language="props.language" :autodetect="!props.language" :code="props.code" />
</div>
</template>
@ -54,8 +54,8 @@
}*/
/** 滚动条 */
:deep(.hljs,.hljs-container) {
max-height: 300px!important;
:deep(.hljs, .hljs-container) {
max-height: 300px !important;
overflow-x: auto;
}

View File

@ -37,46 +37,50 @@ const app = createApp(App)
app.use(vueEsign)
// 局部
import vueEsign from 'vue-esign'
components: { vueEsign }
// vue3 中只需需引入组件就可以使用无需注册
```
2. 页面中使用
**必须设置 `ref` ,用来调用组件的两个内置方法 `reset()``generate()`**
// 在组件中使用 ref="esign"
**在script中必须设置 `const esign = ref()` ,用来调用组件的两个内置方法 `reset()``generate()`**
无需给组件设置 `style` 的宽高,如果画布的 `width`属性值没超出父元素的样式宽度则该组件的样式宽度就是画布宽度超出的话组件样式宽度则是父元素的100% 所以只需设置好父元素的宽度即可;
```html
<!-- vue2 -->
<vue-esign ref="esign" :width="800" :height="300" :isCrop="isCrop" :lineWidth="lineWidth" :lineColor="lineColor" :bgColor.sync="bgColor" />
<!-- vue3 -->
<vue-esign ref="esign" :width="800" :height="300" :isCrop="isCrop" :lineWidth="lineWidth" :lineColor="lineColor" v-model:bgColor="bgColor" />
<vue-esign ref="esign" :width="800" :height="300" :isCrop="isCrop" :lineWidth="lineWidth" :lineColor="lineColor" v-model:bgColor="bgColor" :quality="1"/>
<!-- isClearBgColor为false时不必再给bgColor加sync修饰符或v-model -->
<button @click="handleReset">清空画板</button>
<button @click="handleGenerate">生成图片</button>
```
```js
data () {
return {
lineWidth: 6,
lineColor: '#000000',
bgColor: '',
resultImg: '',
isCrop: false
}
},
methods: {
handleReset () {
this.$refs.esign.reset()
},
handleGenerate () {
this.$refs.esign.generate().then(res => {
this.resultImg = res
}).catch(err => {
alert(err) // 画布没有签字时会执行这里 'Not Signned'
})
}
}
<a-button type="primary" @click="handleGenerate">预览</a-button>
<a-button @click="handleReset">清屏</a-button>
```
```vue
<script setup>
const esign = ref(false) // 相当于$ref
// 这里const相当于vue2中的data()
const resultImg = ref('')
const isCrop = ref(false)
const lineWidth = ref(6)
const lineColor = ref('#000000')
const bgColor = ref('')
const handleReset = () => {
esign.value.reset()
resultImg.value = ''
}
const handleGenerate = () => {
esign.value
.generate()
.then((res) => {
resultImg.value = res
})
.catch(() => {
message.warning('无任何签字') // 画布没有签字时会执行这里 'Not Signned'
})
}
</script>
3. 说明
| 属性 | 类型 | 默认值 | 说明 |
@ -95,7 +99,7 @@ methods: {
**清空画布**
```js
this.$refs.esign.reset()
esign.value.reset()
```
**生成图片**
@ -103,7 +107,7 @@ this.$refs.esign.reset()
```js
// 可选配置参数 在未设置format或quality属性时可在生成图片时配置 例如: {format:'image/jpeg', quality: 0.5}
// this.$refs.esign.generate({format:'image/jpeg', quality: 0.5})
this.$refs.esign.generate().then(res => {
esign.value.generate().then(res => {
console.log(res) // base64图片
}).catch(err => {
alert(err) // 画布没有签字时会执行这里 'Not Signned'

View File

@ -1,7 +1,3 @@
<!--
本插件来源于https://github.com/JaimeCheng/vue-esign#readme
因为集成进来跟我的Vue版本不一致打包出问题所以集成源码方式感谢作者的源码
-->
<template>
<canvas
ref="canvas"
@ -14,283 +10,283 @@
></canvas>
</template>
<script>
export default {
props: {
width: {
type: Number,
default: 800
},
height: {
type: Number,
default: 300
},
lineWidth: {
type: Number,
default: 4
},
lineColor: {
type: String,
default: '#000000'
},
bgColor: {
type: String,
default: ''
},
isCrop: {
type: Boolean,
default: false
},
isClearBgColor: {
type: Boolean,
default: true
},
format: {
type: String,
default: 'image/png'
},
quality: {
type: Number,
default: 1
}
},
data() {
return {
hasDrew: false,
resultImg: '',
points: [],
canvasTxt: null,
startX: 0,
startY: 0,
isDrawing: false,
sratio: 1
}
},
computed: {
ratio() {
return this.height / this.width
},
stageInfo() {
return this.$refs.canvas.getBoundingClientRect()
},
myBg() {
return this.bgColor ? this.bgColor : 'rgba(255, 255, 255, 0)'
}
},
watch: {
myBg: function (newVal) {
this.$refs.canvas.style.background = newVal
}
},
beforeMount() {
window.addEventListener('resize', this.$_resizeHandler)
},
// eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle
beforeDestroy() {
window.removeEventListener('resize', this.$_resizeHandler)
},
mounted() {
const canvas = this.$refs.canvas
canvas.height = this.height
canvas.width = this.width
canvas.style.background = this.myBg
this.$_resizeHandler()
//
document.onmouseup = () => {
this.isDrawing = false
}
},
methods: {
$_resizeHandler() {
const canvas = this.$refs.canvas
canvas.style.width = this.width + 'px'
const realw = parseFloat(window.getComputedStyle(canvas).width)
canvas.style.height = this.ratio * realw + 'px'
this.canvasTxt = canvas.getContext('2d')
this.canvasTxt.scale(Number(this.sratio), Number(this.sratio))
this.sratio = realw / this.width
this.canvasTxt.scale(1 / this.sratio, 1 / this.sratio)
},
// pc
mouseDown(e) {
e = e || event
e.preventDefault()
this.isDrawing = true
this.hasDrew = true
let obj = {
x: e.offsetX,
y: e.offsetY
}
this.drawStart(obj)
},
mouseMove(e) {
e = e || event
e.preventDefault()
if (this.isDrawing) {
let obj = {
x: e.offsetX,
y: e.offsetY
}
this.drawMove(obj)
}
},
mouseUp(e) {
e = e || event
e.preventDefault()
let obj = {
x: e.offsetX,
y: e.offsetY
}
this.drawEnd(obj)
this.isDrawing = false
},
// mobile
touchStart(e) {
e = e || event
e.preventDefault()
this.hasDrew = true
if (e.touches.length === 1) {
let obj = {
x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
}
this.drawStart(obj)
}
},
touchMove(e) {
e = e || event
e.preventDefault()
if (e.touches.length === 1) {
let obj = {
x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
}
this.drawMove(obj)
}
},
touchEnd(e) {
e = e || event
e.preventDefault()
if (e.touches.length === 1) {
let obj = {
x: e.targetTouches[0].clientX - this.$refs.canvas.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - this.$refs.canvas.getBoundingClientRect().top
}
this.drawEnd(obj)
}
},
//
drawStart(obj) {
this.startX = obj.x
this.startY = obj.y
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.lineCap = 'round'
this.canvasTxt.lineJoin = 'round'
this.canvasTxt.lineWidth = this.lineWidth * this.sratio
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.points.push(obj)
},
drawMove(obj) {
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.strokeStyle = this.lineColor
this.canvasTxt.lineWidth = this.lineWidth * this.sratio
this.canvasTxt.lineCap = 'round'
this.canvasTxt.lineJoin = 'round'
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.startY = obj.y
this.startX = obj.x
this.points.push(obj)
},
drawEnd(obj) {
this.canvasTxt.beginPath()
this.canvasTxt.moveTo(this.startX, this.startY)
this.canvasTxt.lineTo(obj.x, obj.y)
this.canvasTxt.lineCap = 'round'
this.canvasTxt.lineJoin = 'round'
this.canvasTxt.stroke()
this.canvasTxt.closePath()
this.points.push(obj)
this.points.push({ x: -1, y: -1 })
},
//
generate(options) {
let imgFormat = options && options.format ? options.format : this.format
let imgQuality = options && options.quality ? options.quality : this.quality
const pm = new Promise((resolve, reject) => {
if (!this.hasDrew) {
reject(`Warning: Not Signned!`)
return
}
var resImgData = this.canvasTxt.getImageData(0, 0, this.$refs.canvas.width, this.$refs.canvas.height)
this.canvasTxt.globalCompositeOperation = 'destination-over'
this.canvasTxt.fillStyle = this.myBg
this.canvasTxt.fillRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height)
this.resultImg = this.$refs.canvas.toDataURL(imgFormat, imgQuality)
var resultImg = this.resultImg
this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height)
this.canvasTxt.putImageData(resImgData, 0, 0)
this.canvasTxt.globalCompositeOperation = 'source-over'
if (this.isCrop) {
const crop_area = this.getCropArea(resImgData.data)
var crop_canvas = document.createElement('canvas')
const crop_ctx = crop_canvas.getContext('2d')
crop_canvas.width = crop_area[2] - crop_area[0]
crop_canvas.height = crop_area[3] - crop_area[1]
const crop_imgData = this.canvasTxt.getImageData(...crop_area)
crop_ctx.globalCompositeOperation = 'destination-over'
crop_ctx.putImageData(crop_imgData, 0, 0)
crop_ctx.fillStyle = this.myBg
crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height)
resultImg = crop_canvas.toDataURL(imgFormat, imgQuality)
crop_canvas = null
}
resolve(resultImg)
})
return pm
},
reset() {
this.canvasTxt.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height)
if (this.isClearBgColor) {
this.$emit('update:bgColor', '')
this.$refs.canvas.style.background = 'rgba(255, 255, 255, 0)'
}
this.points = []
this.hasDrew = false
this.resultImg = ''
},
getCropArea(imgData) {
var topX = this.$refs.canvas.width
var btmX = 0
var topY = this.$refs.canvas.height
var btnY = 0
for (var i = 0; i < this.$refs.canvas.width; i++) {
for (var j = 0; j < this.$refs.canvas.height; j++) {
var pos = (i + this.$refs.canvas.width * j) * 4
if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
btnY = Math.max(j, btnY)
btmX = Math.max(i, btmX)
topY = Math.min(j, topY)
topX = Math.min(i, topX)
}
}
}
topX++
btmX++
topY++
btnY++
const data = [topX, topY, btmX, btnY]
return data
<script setup>
const canvas = ref()
const emit = defineEmits(['update:bgColor'])
const props = defineProps({
width: {
type: Number,
default: 800
},
height: {
type: Number,
default: 300
},
lineWidth: {
type: Number,
default: 4
},
lineColor: {
type: String,
default: '#000000'
},
bgColor: {
type: String,
default: ''
},
isCrop: {
type: Boolean,
default: false
},
isClearBgColor: {
type: Boolean,
default: true
},
format: {
type: String,
default: 'image/png'
},
quality: {
type: Number,
default: 1
}
})
const hasDrew = ref(false)
const resultImg = ref('')
const points = ref([])
const canvasTxt = ref(null)
const startX = ref(0)
const startY = ref(0)
const isDrawing = ref(false)
const sratio = ref(1)
const ratio = computed(() => {
return props.height / props.width
})
const stageInfo = computed(() => {
return canvas.value.getBoundingClientRect()
})
const myBg = computed(() => {
return props.bgColor ? props.bgColor : 'rgba(255, 255, 255, 0)'
})
watch(myBg, (newVal) => {
canvas.value.style.background = newVal
})
onBeforeMount(() => {
window.addEventListener('resize', $_resizeHandler)
})
onBeforeUnmount(() => {
window.removeEventListener('resize', $_resizeHandler)
})
onMounted(() => {
canvas.value.height = props.height
canvas.value.width = props.width
canvas.value.style.background = myBg.value
$_resizeHandler()
//
document.onmouseup = () => {
isDrawing.value = false
}
})
const $_resizeHandler = () => {
canvas.value.style.width = props.width + 'px'
const realw = parseFloat(window.getComputedStyle(canvas.value).width)
canvas.value.style.height = ratio.value * realw + 'px'
canvasTxt.value = canvas.value.getContext('2d', { willReadFrequently: true })
canvasTxt.value.scale(Number(sratio.value), Number(sratio.value))
sratio.value = realw / props.width
canvasTxt.value.scale(1 / sratio.value, 1 / sratio.value)
}
// pc
const mouseDown = (e) => {
e = e || event
e.preventDefault()
isDrawing.value = true
hasDrew.value = true
let obj = {
x: e.offsetX,
y: e.offsetY
}
drawStart(obj)
}
const mouseMove = (e) => {
e = e || event
e.preventDefault()
if (isDrawing.value) {
let obj = {
x: e.offsetX,
y: e.offsetY
}
drawMove(obj)
}
}
</script>
const mouseUp = (e) => {
e = e || event
e.preventDefault()
let obj = {
x: e.offsetX,
y: e.offsetY
}
drawEnd(obj)
isDrawing.value = false
}
// mobile
const touchStart = (e) => {
e = e || event
e.preventDefault()
hasDrew.value = true
if (e.touches.length === 1) {
let obj = {
x: e.targetTouches[0].clientX - canvas.value.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - canvas.value.getBoundingClientRect().top
}
drawStart(obj)
}
}
const touchMove = (e) => {
e = e || event
e.preventDefault()
if (e.touches.length === 1) {
let obj = {
x: e.targetTouches[0].clientX - canvas.value.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - canvas.value.getBoundingClientRect().top
}
drawMove(obj)
}
}
const touchEnd = (e) => {
e = e || event
e.preventDefault()
if (e.touches.length === 1) {
let obj = {
x: e.targetTouches[0].clientX - canvas.value.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - canvas.value.getBoundingClientRect().top
}
drawEnd(obj)
}
}
//
const drawStart = (obj) => {
startX.value = obj.x
startY.value = obj.y
canvasTxt.value.beginPath()
canvasTxt.value.moveTo(startX.value, startY.value)
canvasTxt.value.lineTo(obj.x, obj.y)
canvasTxt.value.lineCap = 'round'
canvasTxt.value.lineJoin = 'round'
canvasTxt.value.lineWidth = props.lineWidth * sratio.value
canvasTxt.value.stroke()
canvasTxt.value.closePath()
points.value.push(obj)
}
const drawMove = (obj) => {
canvasTxt.value.beginPath()
canvasTxt.value.moveTo(startX.value, startY.value)
canvasTxt.value.lineTo(obj.x, obj.y)
canvasTxt.value.strokeStyle = props.lineColor
canvasTxt.value.lineWidth = props.lineWidth * sratio.value
canvasTxt.value.lineCap = 'round'
canvasTxt.value.lineJoin = 'round'
canvasTxt.value.stroke()
canvasTxt.value.closePath()
startY.value = obj.y
startX.value = obj.x
points.value.push(obj)
}
const drawEnd = (obj) => {
canvasTxt.value.beginPath()
canvasTxt.value.moveTo(startX.value, startY.value)
canvasTxt.value.lineTo(obj.x, obj.y)
canvasTxt.value.lineCap = 'round'
canvasTxt.value.lineJoin = 'round'
canvasTxt.value.stroke()
canvasTxt.value.closePath()
points.value.push(obj)
points.value.push({ x: -1, y: -1 })
}
//
const generate = (options) => {
let imgFormat = options && options.format ? options.format : props.format
let imgQuality = options && options.quality ? options.quality : props.quality
const pm = new Promise((resolve, reject) => {
if (!hasDrew.value) {
reject(`Warning: Not Signned!`)
return
}
var resImgData = canvasTxt.value.getImageData(0, 0, canvas.value.width, canvas.value.height)
canvasTxt.value.globalCompositeOperation = 'destination-over'
canvasTxt.value.fillStyle = myBg.value
canvasTxt.value.fillRect(0, 0, canvas.value.width, canvas.value.height)
resultImg.value = canvas.value.toDataURL(imgFormat, imgQuality)
var resultImgData = resultImg.value
canvasTxt.value.clearRect(0, 0, canvas.value.width, canvas.value.height)
canvasTxt.value.putImageData(resImgData, 0, 0)
canvasTxt.value.globalCompositeOperation = 'source-over'
if (props.isCrop) {
const crop_area = getCropArea(resImgData.data)
var crop_canvas = document.createElement('canvas')
const crop_ctx = crop_canvas.getContext('2d', { willReadFrequently: true })
crop_canvas.width = crop_area[2] - crop_area[0]
crop_canvas.height = crop_area[3] - crop_area[1]
const crop_imgData = canvasTxt.value.getImageData(...crop_area)
crop_ctx.globalCompositeOperation = 'destination-over'
crop_ctx.putImageData(crop_imgData, 0, 0)
crop_ctx.fillStyle = myBg.value
crop_ctx.fillRect(0, 0, crop_canvas.width, crop_canvas.height)
resultImgData = crop_canvas.toDataURL(imgFormat, imgQuality)
crop_canvas = null
}
resolve(resultImgData)
})
return pm
}
const reset = () => {
canvasTxt.value.clearRect(0, 0, canvas.value.width, canvas.value.height)
if (props.isClearBgColor) {
emit('update:bgColor', '')
canvas.value.style.background = 'rgba(255, 255, 255, 0)'
}
points.value = []
hasDrew.value = false
resultImg.value = ''
}
const getCropArea = (imgData) => {
let topX = canvas.value.width
let btmX = 0
let topY = canvas.value.height
let btnY = 0
for (let i = 0; i < canvas.value.width; i++) {
for (let j = 0; j < canvas.value.height; j++) {
let pos = (i + canvas.value.width * j) * 4
if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
btnY = Math.max(j, btnY)
btmX = Math.max(i, btmX)
topY = Math.min(j, topY)
topX = Math.min(i, topX)
}
}
}
topX++
btmX++
topY++
btnY++
return [topX, topY, btmX, btnY]
}
// 使
defineExpose({
generate,
reset
})
</script>
<style scoped>
canvas {
max-width: 100%;

View File

@ -8,8 +8,6 @@
* 5.不可二次分发开源参与同类竞品如有想法可联系团队xiaonuobase@qq.com商议合作
* 6.若您的项目无法满足以上几点需要更多功能代码获取Snowy商业授权许可请在官网购买授权地址为 https://www.xiaonuo.vip
*/
import { defineAsyncComponent } from 'vue'
/**
* 图标选择器基础数据
* 推荐前往https://icones.js.org下载图标的Vue文件然后放在src/assets/icons文件夹里面

View File

@ -12,7 +12,7 @@
:href="navMenu.path"
target="_blank"
@click.stop="() => {}"
>{{ navMenu.meta.title }}</a
>{{ navMenu.meta.title }}</a
>
<a v-else>{{ navMenu.meta.title }}</a>
</a-menu-item>

View File

@ -1,49 +1,53 @@
<template>
<div v-show="$route.meta.type === 'iframe'" class="iframe-pages">
<div v-show="route.meta.type === 'iframe'" class="iframe-pages">
<div></div>
<iframe
v-for="item in iframeList"
v-show="$route.meta.url === item.meta.url"
v-show="route.meta.url === item.meta.url"
:key="item.meta.url"
:src="item.meta.url"
frameborder="0"
></iframe>
</div>
</template>
<script>
import { mapState, mapActions } from 'pinia'
<script setup>
import { useRoute, useRouter } from 'vue-router'
import { iframeStore, globalStore } from '@/store'
const iStore = iframeStore()
const store = globalStore()
const route = useRoute()
const router = useRouter()
export default {
data() {
return {}
},
computed: {
...mapState(iframeStore, ['iframeList']),
...mapState(globalStore, ['isMobile', 'layoutTags'])
},
watch: {
$route(e) {
this.push(e)
}
},
created() {
this.push(this.$route)
},
mounted() {},
methods: {
...mapActions(iframeStore, ['setIframeList', 'pushIframeList', 'clearIframeList']),
push(route) {
if (route.meta.type === 'iframe') {
if (this.isMobile || !this.layoutTags) {
this.setIframeList(route)
} else {
this.pushIframeList(route)
}
} else if (this.isMobile || !this.layoutTags) {
this.clearIframeList()
}
const iframeList = computed(() => {
return iStore.iframeList
})
const isMobile = computed(() => {
return store.isMobile
})
const layoutTags = computed(() => {
return store.layoutTags
})
watch(route, () => {
push(router.currentRoute.value)
})
onBeforeMount(() => {
push(router.currentRoute.value)
})
const setIframeList = iStore.setIframeList
const pushIframeList = iStore.pushIframeList
const clearIframeList = iStore.clearIframeList
const push = (route) => {
if (route.meta.type === 'iframe') {
if (isMobile || !layoutTags) {
setIframeList(route)
} else {
pushIframeList(route)
}
} else if (isMobile || !layoutTags) {
clearIframeList()
}
}
</script>

View File

@ -33,7 +33,7 @@
</a-form-item>
<a-form-item label="查收情况:" name="receiveInfoList">
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:alert="false"
@ -146,7 +146,7 @@
Object.assign(message, data)
formData.value = message
receiveInfoList.value = data.receiveInfoList
table.value.refresh(true)
tableRef.value.refresh(true)
})
unreadMessageNum.value = Math.max(unreadMessageNum.value - 1, 0)
}
@ -161,7 +161,7 @@
const formRef = ref()
const receiveInfoList = ref([])
const formData = ref({})
const table = ref()
const tableRef = ref()
const columns = [
{
title: '姓名',

View File

@ -97,6 +97,7 @@
moduleBackColor.value
? moduleMenu.classList.add('module-menu-color')
: moduleMenu.classList.remove('module-menu-color')
// eslint-disable-next-line no-empty
} catch (err) {}
setSelectedKeys()
}

View File

@ -53,7 +53,7 @@
/>
</div>
<a-divider />
<a-form ref="form" class="text-right">
<a-form ref="formRef" class="text-right">
<a-form-item label="模块坞">
<a-switch :checked="moduleUnfoldOpen" @change="toggleState('moduleUnfoldOpen')" />
</a-form-item>
@ -86,14 +86,12 @@
</div>
</div>
</template>
<script>
<script setup>
import { colorList } from '@/config/settingConfig'
import { ThemeModeEnum } from '@/utils/enum'
import { globalStore } from '@/store'
import { mapState, mapStores } from 'pinia'
import tool from '@/utils/tool'
const store = globalStore()
const toolDataNameMap = {
menuIsCollapse: 'MENU_COLLAPSE',
sideUniqueOpen: 'SIDE_UNIQUE_OPEN',
@ -103,109 +101,121 @@
topHeaderThemeColorSpread: 'TOP_HEADER_THEME_COLOR_SPREAD',
moduleUnfoldOpen: 'MODULE_UNFOLD_OPEN'
}
export default defineComponent({
data() {
return {
sideStyleList: [
{
tips: '暗色主题风格',
value: ThemeModeEnum.DARK,
style: 'snowy-setting-checkbox-item-dark'
},
{
tips: '亮色主题风格',
value: ThemeModeEnum.LIGHT,
style: 'snowy-setting-checkbox-item-light'
},
{
tips: '暗黑模式',
value: ThemeModeEnum.REAL_DARK,
style: 'snowy-setting-checkbox-item-realdark'
}
],
layoutList: [
{
tips: '经典',
value: 'classical',
style: 'snowy-setting-layout-menu-classical'
},
{
tips: '双排菜单',
value: 'doublerow',
style: 'snowy-setting-layout-menu-doublerow'
}
],
xnFormStyleOptions: [
{
label: '抽屉',
value: 'drawer'
},
{
label: '对话框',
value: 'modal'
}
],
colorList
}
const sideStyleList = ref([
{
tips: '暗色主题风格',
value: ThemeModeEnum.DARK,
style: 'snowy-setting-checkbox-item-dark'
},
computed: {
...mapStores(globalStore),
...mapState(globalStore, [
'theme',
'themeColor',
'layout',
'menuIsCollapse',
'sideUniqueOpen',
'layoutTagsOpen',
'breadcrumbOpen',
'moduleUnfoldOpen',
'topHeaderThemeColorOpen',
'topHeaderThemeColorSpread',
'formStyle'
])
{
tips: '亮色主题风格',
value: ThemeModeEnum.LIGHT,
style: 'snowy-setting-checkbox-item-light'
},
mounted() {},
methods: {
changeTopHanderThemeColorOpen() {
this.toggleState('topHeaderThemeColorOpen')
if (!this.topHeaderThemeColorOpen) {
this.globalStore.topHeaderThemeColorSpread = false
tool.data.set('SNOWY_TOP_HEADER_THEME_COLOR_SPREAD', false)
}
},
changeTopHanderThemeColorSpread() {
this.toggleState('topHeaderThemeColorSpread')
},
toggleState(stateName) {
this.globalStore.toggleConfig(stateName)
const toolDataName = toolDataNameMap[stateName]
tool.data.set(`SNOWY_${toolDataName}`, this.globalStore[stateName])
},
//
setSideStyle(value) {
this.globalStore.setTheme(value)
tool.data.set('SNOWY_THEME', value)
},
//
layoutStyle(value) {
this.globalStore.setLayout(value)
tool.data.set('SNOWY_LAYOUT', value)
},
//
tagColor(value) {
tool.data.set('SNOWY_THEME_COLOR', value)
this.globalStore.setThemeColor(value)
},
//
formStyleChange(value) {
tool.data.set('SNOWY_FORM_STYLE', value)
this.globalStore.setFormStyle(value)
}
{
tips: '暗黑模式',
value: ThemeModeEnum.REAL_DARK,
style: 'snowy-setting-checkbox-item-realdark'
}
])
const layoutList = ref([
{
tips: '经典',
value: 'classical',
style: 'snowy-setting-layout-menu-classical'
},
{
tips: '双排菜单',
value: 'doublerow',
style: 'snowy-setting-layout-menu-doublerow'
}
])
const xnFormStyleOptions = ref([
{
label: '抽屉',
value: 'drawer'
},
{
label: '对话框',
value: 'modal'
}
])
const theme = computed(() => {
return store.theme
})
const themeColor = computed(() => {
return store.themeColor
})
const layout = computed(() => {
return store.layout
})
const menuIsCollapse = computed(() => {
return store.menuIsCollapse
})
const sideUniqueOpen = computed(() => {
return store.sideUniqueOpen
})
const layoutTagsOpen = computed(() => {
return store.layoutTagsOpen
})
const breadcrumbOpen = computed(() => {
return store.breadcrumbOpen
})
const moduleUnfoldOpen = computed(() => {
return store.moduleUnfoldOpen
})
const topHeaderThemeColorOpen = computed(() => {
return store.topHeaderThemeColorOpen
})
const topHeaderThemeColorSpread = computed(() => {
return store.topHeaderThemeColorSpread
})
const formStyle = computed(() => {
return store.formStyle
})
const changeTopHanderThemeColorOpen = () => {
toggleState('topHeaderThemeColorOpen')
if (!topHeaderThemeColorOpen) {
store.topHeaderThemeColorSpread = false
tool.data.set('SNOWY_TOP_HEADER_THEME_COLOR_SPREAD', false)
}
}
const changeTopHanderThemeColorSpread = () => {
toggleState('topHeaderThemeColorSpread')
}
const toggleState = (stateName) => {
store.toggleConfig(stateName)
const toolDataName = toolDataNameMap[stateName]
tool.data.set(`SNOWY_${toolDataName}`, store[stateName])
}
//
const setSideStyle = (value) => {
store.setTheme(value)
tool.data.set('SNOWY_THEME', value)
}
//
const layoutStyle = (value) => {
store.setLayout(value)
tool.data.set('SNOWY_LAYOUT', value)
}
//
const tagColor = (value) => {
tool.data.set('SNOWY_THEME_COLOR', value)
store.setThemeColor(value)
}
//
const formStyleChange = (value) => {
tool.data.set('SNOWY_FORM_STYLE', value)
store.setFormStyle(value)
}
</script>
<style type="less" scoped>
<style lang="less" scoped>
.snowy-setting-checkbox {
display: flex;
margin-bottom: 20px;

View File

@ -16,108 +16,100 @@
</a-menu>
</a-drawer>
</template>
<script>
<script setup>
import NavMenu from './NavMenu.vue'
import { globalStore } from '@/store'
import { mapState } from 'pinia'
import { useRouter } from 'vue-router'
export default {
components: {
NavMenu
},
directives: {
drag(el) {
const oDiv = el //
let firstTime = ''
let lastTime = ''
//
// document.onselectstart = function() {
// return false;
// };
oDiv.onmousedown = function (e) {
//
const disX = e.clientX - oDiv.offsetLeft
const disY = e.clientY - oDiv.offsetTop
document.onmousemove = function (e) {
oDiv.setAttribute('drag-flag', true)
firstTime = new Date().getTime()
//
const l = e.clientX - disX
const t = e.clientY - disY
//
if (t > 0 && t < document.body.clientHeight - 50) {
oDiv.style.top = `${t}px`
}
if (l > 0 && l < document.body.clientWidth - 50) {
oDiv.style.left = `${l}px`
}
}
document.onmouseup = function () {
lastTime = new Date().getTime()
if (lastTime - firstTime > 200) {
oDiv.setAttribute('drag-flag', false)
}
document.onmousemove = null
document.onmouseup = null
}
// return falsedivonmouseup
return false
const router = useRouter()
const store = globalStore()
const vDrag = (el) => {
const oDiv = el //
let firstTime = ''
let lastTime = ''
//
// document.onselectstart = function() {
// return false;
// };
oDiv.onmousedown = function (e) {
//
const disX = e.clientX - oDiv.offsetLeft
const disY = e.clientY - oDiv.offsetTop
document.onmousemove = function (e) {
oDiv.setAttribute('drag-flag', true)
firstTime = new Date().getTime()
//
const l = e.clientX - disX
const t = e.clientY - disY
//
if (t > 0 && t < document.body.clientHeight - 50) {
oDiv.style.top = `${t}px`
}
if (l > 0 && l < document.body.clientWidth - 50) {
oDiv.style.left = `${l}px`
}
}
},
data() {
return {
visible: false,
menu: []
}
},
computed: {
...mapState(globalStore, ['sysBaseConfig'])
},
created() {
const menu = this.$router.getMenu()
this.menu = this.filterUrl(menu)
},
methods: {
showMobileNav(e) {
const isdrag = e.currentTarget.getAttribute('drag-flag')
this.visible = true
if (isdrag === 'true') {
return false
} else {
this.visible = true
document.onmouseup = function () {
lastTime = new Date().getTime()
if (lastTime - firstTime > 200) {
oDiv.setAttribute('drag-flag', false)
}
},
//
onSelect(obj) {
const pathLength = obj.keyPath.length
const path = obj.keyPath[pathLength - 1]
this.$router.push({ path })
this.visible = false
},
//
filterUrl(map) {
map.forEach((item, index) => {
item.meta = item.meta ? item.meta : {}
//
if (item.meta.hidden) {
map.splice(index, 1)
}
// http
// eslint-disable-next-line eqeqeq
if (item.meta.type == 'iframe') {
item.path = `/i/${item.name}`
}
//
if (item.children && item.children.length > 0) {
item.children = this.filterUrl(item.children)
}
})
return map
document.onmousemove = null
document.onmouseup = null
}
// return falsedivonmouseup
return false
}
}
const visible = ref(false)
const menu = ref([])
const sysBaseConfig = computed(() => {
return store.sysBaseConfig
})
onBeforeMount(() => {
const menus = router.getMenu()
menu.value = filterUrl(menus)
})
const showMobileNav = (e) => {
const isdrag = e.currentTarget.getAttribute('drag-flag')
visible.value = true
if (isdrag === 'true') {
return false
} else {
visible.value = true
}
}
//
const onSelect = (obj) => {
const pathLength = obj.keyPath.length
const path = obj.keyPath[pathLength - 1]
router.push({ path })
visible.value = false
}
//
const filterUrl = (map) => {
map.forEach((item, index) => {
item.meta = item.meta ? item.meta : {}
//
if (item.meta.hidden) {
map.splice(index, 1)
}
// http
if (item.meta.type === 'iframe') {
item.path = `/i/${item.name}`
}
//
if (item.children && item.children.length > 0) {
item.children = filterUrl(item.children)
}
})
return map
}
</script>
<style lang="less" scoped>

View File

@ -58,242 +58,245 @@
</div>
</template>
<script>
import tool from '@/utils/tool'
<script setup>
import XnContextMenu from '@/components/XnContextMenu/index.vue'
import { globalStore, iframeStore, keepAliveStore, viewTagsStore } from '@/store'
import { mapState, mapActions } from 'pinia'
import routerUtil from '@/utils/routerUtil'
import { useRoute, useRouter } from 'vue-router'
import { watch } from 'vue'
export default {
name: 'Tags',
components: { XnContextMenu },
props: {},
data() {
return {
// tagList: [],
activeKey: this.$route.fullPath,
maxTabs: 20,
contextMenuTarget: null,
contextMenuVisible: false,
currentContextMenuTabIndex: 0
const route = useRoute()
const router = useRouter()
const store = globalStore()
const kStore = keepAliveStore()
const iStore = iframeStore()
const vStore = viewTagsStore()
const activeKey = ref()
const maxTabs = ref(20)
const contextMenuTarget = ref(null)
const contextMenuVisible = ref(false)
const currentContextMenuTabIndex = ref(0)
const viewTags = computed(() => {
return vStore.viewTags
})
const layoutTagsOpen = computed(() => {
return store.layoutTagsOpen
})
const tagList = computed(() => {
return viewTags.value
})
watch(route, (to) => {
addViewTags(to)
activeKey.value = to.fullPath
})
watch(layoutTagsOpen, () => {
// closeOtherCacheTabs()
})
onMounted(() => {
const tabNavList = document.querySelector('.ant-tabs-nav-list')
if (tabNavList) {
contextMenuTarget.value = tabNavList
}
})
// tag
const addViewTags = (to) => {
activeKey.value = to.fullPath
if (to.name && !to.meta.fullpage) {
vStore.pushViewTags(to)
kStore.pushKeepLive(to.name)
}
if (tagList.value.length - 1 > maxTabs.value) {
const firstTag = tagList.value[1]
vStore.removeViewTags(firstTag)
}
}
//
const treeFind = (tree, func) => {
for (const data of tree) {
if (func(data)) return data
if (data.children) {
const res = treeFind(data.children, func)
if (res) return res
}
},
computed: {
...mapState(viewTagsStore, ['viewTags']),
...mapState(globalStore, ['layoutTagsOpen']),
tagList() {
return this.viewTags
}
},
watch: {
$route(to) {
this.addViewTags(to)
},
layoutTagsOpen() {
this.closeOtherCacheTabs()
}
},
created() {
const module = this.$router.getMenu()
const indexMenu = routerUtil.getIndexMenu(module).path
// eslint-disable-next-line eqeqeq
const dashboardRoute = this.treeFind(module, (node) => node.path === indexMenu)
if (dashboardRoute) {
dashboardRoute.fullPath = dashboardRoute.path
this.addViewTags(dashboardRoute)
this.addViewTags(this.$route)
}
},
mounted() {
const tabNavList = document.querySelector('.ant-tabs-nav-list')
if (tabNavList) {
this.contextMenuTarget = tabNavList
}
},
methods: {
...mapActions(viewTagsStore, ['addViewTags', 'pushViewTags', 'removeViewTags']),
...mapActions(iframeStore, ['addIframe', 'removeIframeList', 'refreshIframe']),
...mapActions(keepAliveStore, ['pushKeepLive', 'removeKeepLive', 'setRouteShow']),
handleTabContextMenu(evt) {
evt.preventDefault()
let target = evt.target
// "使"
while (!target.classList.contains('ant-tabs-tab')) {
if (target.classList.contains('ant-tabs')) {
this.currentContextMenuTabIndex = 0
return
}
target = target.parentNode
}
const tabList = document.querySelectorAll('.ant-tabs-nav-list .ant-tabs-tab')
this.currentContextMenuTabIndex = Array.from(tabList).findIndex((tab) => tab === target)
},
onTabClick(tab) {
this.$router.push(tab)
},
getCurrentTag() {
return this.tagList[this.currentContextMenuTabIndex]
},
onTabRemove(tabKey, action) {
if (action === 'remove') {
const tag = this.tagList.find((tag) => tag.fullPath === tabKey)
this.closeSelectedTag(tag)
}
},
//
onTabUp(e) {
//
if (e.which === 2) {
this.handleTabContextMenu(e)
this.closeTabs()
}
},
getTabWrapEl() {
return document.querySelector('.ant-tabs-nav-wrap')
},
scrollLeft() {
const wrapEl = this.getTabWrapEl()
if (wrapEl) {
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 })
wrapEl.dispatchEvent(event)
}
},
scrollRight() {
const wrapEl = this.getTabWrapEl()
if (wrapEl) {
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: 100 })
wrapEl.dispatchEvent(event)
}
},
//
treeFind(tree, func) {
for (const data of tree) {
if (func(data)) return data
if (data.children) {
const res = this.treeFind(data.children, func)
if (res) return res
}
}
return null
},
// tag
addViewTags(route) {
this.activeKey = route.fullPath
if (route.name && !route.meta.fullpage) {
this.pushViewTags(route)
this.pushKeepLive(route.name)
}
if (this.tagList.length - 1 > this.maxTabs) {
const firstTag = this.tagList[1]
this.removeViewTags(firstTag)
}
},
// tag
isActive(route) {
return route.path === this.$route.path
},
// tag
closeSelectedTag(tag, autoPushLatestView = true) {
this.removeViewTags(tag)
this.removeIframeList(tag)
this.removeKeepLive(tag.name)
if (autoPushLatestView && this.isActive(tag)) {
const latestView = this.tagList.slice(-1)[0]
if (latestView) {
this.$router.push(latestView)
} else {
this.$router.push('/')
}
}
},
// TAB
refreshTab() {
this.contextMenuVisible = false
const nowTag = this.getCurrentTag()
//
// eslint-disable-next-line eqeqeq
if (this.$route.fullPath !== nowTag.fullPath) {
this.$router.push({
path: nowTag.fullPath,
query: nowTag.query
})
}
this.refreshIframe(nowTag)
setTimeout(() => {
this.removeKeepLive(nowTag.name)
this.setRouteShow(false)
this.$nextTick(() => {
this.pushKeepLive(nowTag.name)
this.setRouteShow(true)
})
}, 0)
},
// TAB
closeTabs() {
this.contextMenuVisible = false
const nowTag = this.getCurrentTag()
if (!nowTag.meta.affix) {
this.closeSelectedTag(nowTag)
}
},
// TAB
closeOtherTabs() {
this.contextMenuVisible = false
const nowTag = this.getCurrentTag()
//
// eslint-disable-next-line eqeqeq
if (this.$route.fullPath !== nowTag.fullPath) {
this.$router.push({
path: nowTag.fullPath,
query: nowTag.query
})
}
const tags = [...this.tagList]
tags.forEach((tag) => {
// eslint-disable-next-line eqeqeq
if ((tag.meta && tag.meta.affix) || nowTag.fullPath === tag.fullPath) {
return true
} else {
this.closeSelectedTag(tag, false)
}
})
},
//
closeOtherCacheTabs() {
const tags = [...this.tagList]
tags.forEach((tag) => {
this.closeSelectedTag(tag, false)
})
},
// TAB
maximize() {
this.contextMenuVisible = false
const nowTag = this.getCurrentTag()
//
// eslint-disable-next-line eqeqeq
if (this.$route.fullPath !== nowTag.fullPath) {
this.$router.push({
path: nowTag.fullPath,
query: nowTag.query
})
}
document.getElementById('app').classList.add('main-maximize')
},
//
openWindow() {
this.contextMenuVisible = false
const nowTag = this.getCurrentTag()
const url = nowTag.href || '/'
if (!nowTag.meta.affix) {
this.closeSelectedTag(nowTag)
}
window.open(url)
}
return null
}
const getCurrentTag = () => {
return tagList.value[currentContextMenuTabIndex.value]
}
// tag
const isActive = (to) => {
return to.path === route.path
}
// tag
const closeSelectedTag = (tag, autoPushLatestView = true) => {
vStore.removeViewTags(tag)
iStore.removeIframeList(tag)
kStore.removeKeepLive(tag.name)
if (autoPushLatestView && isActive(tag)) {
const latestView = tagList.value.slice(-1)[0]
if (latestView) {
router.push(latestView)
} else {
router.push('/')
}
}
}
</script>
// TAB
const refreshTab = () => {
contextMenuVisible.value = false
const nowTag = getCurrentTag()
//
// eslint-disable-next-line eqeqeq
if (route.fullPath !== nowTag.fullPath) {
router.push({
path: nowTag.fullPath,
query: nowTag.query
})
}
iStore.refreshIframe(nowTag)
setTimeout(() => {
kStore.removeKeepLive(nowTag.name)
kStore.setRouteShow(false)
nextTick(() => {
kStore.pushKeepLive(nowTag.name)
kStore.setRouteShow(true)
})
}, 0)
}
// TAB
const closeTabs = () => {
contextMenuVisible.value = false
const nowTag = getCurrentTag()
if (!nowTag.meta.affix) {
closeSelectedTag(nowTag)
}
}
// TAB
const closeOtherTabs = () => {
contextMenuVisible.value = false
const nowTag = getCurrentTag()
//
// eslint-disable-next-line eqeqeq
if (route.fullPath !== nowTag.fullPath) {
router.push({
path: nowTag.fullPath,
query: nowTag.query
})
}
const tags = [...tagList.value]
tags.forEach((tag) => {
// eslint-disable-next-line eqeqeq
if ((tag.meta && tag.meta.affix) || nowTag.fullPath === tag.fullPath) {
return true
} else {
closeSelectedTag(tag, false)
}
})
}
//
const closeOtherCacheTabs = () => {
const tags = [...tagList]
tags.forEach((tag) => {
closeSelectedTag(tag, false)
})
}
// TAB
const maximize = () => {
contextMenuVisible.value = false
const nowTag = getCurrentTag()
//
// eslint-disable-next-line eqeqeq
if (route.fullPath !== nowTag.fullPath) {
router.push({
path: nowTag.fullPath,
query: nowTag.query
})
}
document.getElementById('app').classList.add('main-maximize')
}
//
const openWindow = () => {
contextMenuVisible.value = false
const nowTag = getCurrentTag()
const url = nowTag.path || '/'
if (!nowTag.meta.affix) {
closeSelectedTag(nowTag)
}
window.open(url)
}
const handleTabContextMenu = (evt) => {
evt.preventDefault()
let target = evt.target
// "使"
while (!target.classList.contains('ant-tabs-tab')) {
if (target.classList.contains('ant-tabs')) {
currentContextMenuTabIndex.value = 0
return
}
target = target.parentNode
}
const tabList = document.querySelectorAll('.ant-tabs-nav-list .ant-tabs-tab')
currentContextMenuTabIndex.value = Array.from(tabList).findIndex((tab) => tab === target)
}
const module = router.getMenu()
const indexMenu = routerUtil.getIndexMenu(module).path
// eslint-disable-next-line eqeqeq
const dashboardRoute = treeFind(module, (node) => node.path === indexMenu)
if (dashboardRoute) {
dashboardRoute.fullPath = dashboardRoute.path
addViewTags(dashboardRoute)
addViewTags(route)
}
const onTabRemove = (tabKey, action) => {
if (action === 'remove') {
const tag = tagList.value.find((tag) => tag.fullPath === tabKey)
closeSelectedTag(tag)
}
}
const onTabClick = (tab) => {
router.push(tab)
}
//
const onTabUp = (e) => {
//
if (e.which === 2) {
handleTabContextMenu(e)
closeTabs()
}
}
const getTabWrapEl = () => {
return document.querySelector('.ant-tabs-nav-wrap')
}
const scrollLeft = () => {
const wrapEl = getTabWrapEl()
if (wrapEl) {
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: -100 })
wrapEl.dispatchEvent(event)
}
}
const scrollRight = () => {
const wrapEl = getTabWrapEl()
if (wrapEl) {
const event = new WheelEvent('wheel', { deltaX: 0, deltaY: 100 })
wrapEl.dispatchEvent(event)
}
}
</script>
<style lang="less">
.snowy-admin-tabs {
&.ant-tabs {
@ -316,7 +319,9 @@
background: none;
height: 40px;
line-height: 40px;
transition: background-color 0.3s, color 0.3s;
transition:
background-color 0.3s,
color 0.3s;
padding: 0 16px;
border-radius: 0;
border: none;

View File

@ -15,27 +15,22 @@
</div>
</div>
</template>
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'
<script>
export default {
data() {
return {
breadList: []
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
getBreadcrumb() {
const matched = this.$route.meta.breadcrumb
this.breadList = matched
}
}
const route = useRoute()
const breadList = ref([])
watch(route, () => {
getBreadcrumb()
})
onBeforeMount(() => {
getBreadcrumb()
})
const getBreadcrumb = () => {
breadList.value = route.meta.breadcrumb
}
</script>

View File

@ -53,9 +53,9 @@
<Tags v-if="!isMobile && layoutTagsOpen" />
<a-layout-content class="main-content-wrapper">
<div id="admin-ui-main" class="admin-ui-main">
<router-view v-slot="{ Component }" :key="route.fullPath">
<keep-alive :include="keepLiveRoute">
<component :is="Component" :key="route.name" v-if="routeShow" />
<router-view v-slot="{ Component }">
<keep-alive :include="kStore.keepLiveRoute">
<component :is="Component" v-if="kStore.routeShow" :key="route.name" />
</keep-alive>
</router-view>
<iframe-view />
@ -149,12 +149,12 @@
</div>
</div>
<!-- 多标签 -->
<Tags v-if="!isMobile && layoutTagsOpen"></Tags>
<Tags v-if="!isMobile && layoutTagsOpen" />
<a-layout-content class="main-content-wrapper">
<div id="admin-ui-main" class="admin-ui-main">
<router-view v-slot="{ Component }" :key="route.fullPath">
<keep-alive :include="keepLiveRoute">
<component :is="Component" v-if="routeShow" :key="route.name" />
<router-view v-slot="{ Component }">
<keep-alive :include="kStore.keepLiveRoute">
<component :is="Component" v-if="kStore.routeShow" :key="route.name" />
</keep-alive>
</router-view>
<iframe-view />
@ -215,6 +215,10 @@
return store.theme
})
const layoutTagsOpen = computed(() => {
// keepAlive
if (!store.layoutTagsOpen) {
kStore.keepLiveRoute = []
}
return store.layoutTagsOpen
})
const breadcrumbOpen = computed(() => {
@ -235,12 +239,6 @@
const module = computed(() => {
return store.module
})
const keepLiveRoute = computed(() => {
return kStore.keepLiveRoute
})
const routeShow = computed(() => {
return kStore.routeShow
})
const sideTheme = computed(() => {
return theme.value === ThemeModeEnum.REAL_DARK ? ThemeModeEnum.DARK : theme.value
})
@ -355,6 +353,7 @@
topHeaderThemeColorSpread.value
? headerLogin.classList.add('snowy-header-logo-primary-color')
: headerLogin.classList.remove('snowy-header-logo-primary-color')
// eslint-disable-next-line no-empty
} catch (e) {}
//
if (layout.value === 'doublerow') {
@ -363,6 +362,7 @@
topHeaderThemeColorSpread.value
? snowyDoublerowSideTop.classList.add('snowy-doublerow-side-top-primary-color')
: snowyDoublerowSideTop.classList.remove('snowy-doublerow-side-top-primary-color')
// eslint-disable-next-line no-empty
} catch (e) {}
}
}

View File

@ -1,3 +1,3 @@
<template>
<a-result status="403" title="403" sub-title="访"> </a-result>
<a-result status="403" title="403" sub-title="访" />
</template>

View File

@ -6,16 +6,13 @@
</template>
</a-result>
</template>
<script>
export default {
methods: {
gohome() {
location.href = '/'
},
goback() {
this.$router.go(-1)
}
}
<script setup>
import { useRouter } from 'vue-router'
const router = useRouter()
const gohome = () => {
location.href = '/'
}
const goback = () => {
router.go(-1)
}
</script>

View File

@ -20,7 +20,7 @@ export default {
searchKey: 'Search Key',
imports: 'Import',
more: 'More',
export: 'Export',
export: 'Export'
},
model: {
user: 'user',

View File

@ -22,7 +22,7 @@ export default {
searchKey: '关键词',
imports: '导入',
more: '更多',
export: '导出',
export: '导出'
},
model: {
user: '用户',

View File

@ -109,7 +109,7 @@ router.beforeEach(async (to, from, next) => {
apiMenu[0] = cloneDeep(userRoutes.module[0])
}
const childrenApiMenu = apiMenu[0].children
apiMenu[0].children = [...childrenApiMenu ? childrenApiMenu : [], ...userRoutes.menu]
apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu]
let menuRouter = filterAsyncRouter(apiMenu)
menuRouter = flatAsyncRoutes(menuRouter)
menuRouter.forEach((item) => {
@ -148,7 +148,7 @@ router.getMenu = () => {
apiMenu[0] = cloneDeep(userRoutes.module[0])
}
const childrenApiMenu = apiMenu[0].children
apiMenu[0].children = [...childrenApiMenu ? childrenApiMenu : [], ...userRoutes.menu]
apiMenu[0].children = [...(childrenApiMenu ? childrenApiMenu : []), ...userRoutes.menu]
return filterUrl(apiMenu)
}

View File

@ -31,7 +31,7 @@ export default {
app.use(customIcons)
// 注册代码高亮组件 博客https://blog.csdn.net/weixin_41897680/article/details/124925222
// 注意解决Vue使用highlight.js build打包发布后样式消失问题原因是webpack在打包的时候没有把未被使用的代码打包进去因此在此处引用一下看似无意义实则有用
hljsCommon.highlightAuto('<h1>Highlight.js has been registered successfully!</h1>').value
hljsCommon.highlightAuto('<h1>Highlight.js has been registered successfully!</h1>').value
app.use(hljsVuePlugin)
// 全局代码错误捕捉

View File

@ -30,78 +30,133 @@ const getCacheConfig = (value) => {
/**
* deprecated 请使用 useGlobalStore
*/
export const globalStore = defineStore({
id: 'global',
state: () => ({
// 移动端布局
isMobile: false,
// 布局
layout: getCacheConfig('SNOWY_LAYOUT'),
// 菜单是否折叠 toggle
menuIsCollapse: getCacheConfig('SNOWY_MENU_COLLAPSE'),
// 侧边菜单是否排他展开
sideUniqueOpen: getCacheConfig('SNOWY_SIDE_UNIQUE_OPEN'),
// 多标签栏
layoutTagsOpen: getCacheConfig('SNOWY_LAYOUT_TAGS_OPEN'),
// 是否展示面包屑
breadcrumbOpen: getCacheConfig('SNOWY_BREADCRUMD_OPEN'),
// 顶栏是否应用主题色
topHeaderThemeColorOpen: getCacheConfig('SNOWY_TOP_HEADER_THEME_COLOR_OPEN'),
// 顶栏主题色通栏
topHeaderThemeColorSpread: getCacheConfig('SNOWY_TOP_HEADER_THEME_COLOR_SPREAD'),
// 模块坞
moduleUnfoldOpen: getCacheConfig('SNOWY_MODULE_UNFOLD_OPEN'),
// 主题
theme: getCacheConfig('SNOWY_THEME'),
// 主题颜色
themeColor: toolDataGet('SNOWY_THEME_COLOR') || config.COLOR,
// 整体表单风格
formStyle: getCacheConfig('SNOWY_FORM_STYLE'),
// 用户信息
userInfo: toolDataGet('USER_INFO') || {},
// 系统配置
sysBaseConfig: toolDataGet('SNOWY_SYS_BASE_CONFIG') || config.SYS_BASE_CONFIG,
// 默认应用
module: getCacheConfig('SNOWY_MENU_MODULE_ID')
}),
getters: {},
actions: {
setIsMobile(key) {
this.isMobile = key
},
setLayout(key) {
this.layout = key
},
setTheme(key) {
this.theme = key
const closeMessage = message.loading(`加载中...`)
changeColor(this.themeColor, key).then(closeMessage)
},
setThemeColor(key) {
this.themeColor = key
const closeMessage = message.loading(`加载中...`)
changeColor(key, this.theme).then(closeMessage)
},
initTheme() {
const closeMessage = message.loading(`加载中...`)
changeColor(this.themeColor, this.theme).then(closeMessage)
},
toggleConfig(key) {
this[key] = !this[key]
},
setFormStyle(key) {
this.formStyle = key
},
setUserInfo(key) {
this.userInfo = key
},
setSysBaseConfig(key) {
this.sysBaseConfig = key
},
setModule(key) {
this.module = key
export const globalStore = defineStore('global', () => {
// 利用Vue3组合式APIref()定义state的属性
// function() 定义actions
// computed 定义getters
// 定义state
// 移动端布局
const isMobile = ref(false)
// 布局
const layout = ref(getCacheConfig('SNOWY_LAYOUT'))
// 菜单是否折叠 toggle
const menuIsCollapse = ref(getCacheConfig('SNOWY_MENU_COLLAPSE'))
// 侧边菜单是否排他展开
const sideUniqueOpen = ref(getCacheConfig('SNOWY_SIDE_UNIQUE_OPEN'))
// 多标签栏
const layoutTagsOpen = ref(getCacheConfig('SNOWY_LAYOUT_TAGS_OPEN'))
// 是否展示面包屑
const breadcrumbOpen = ref(getCacheConfig('SNOWY_BREADCRUMD_OPEN'))
// 顶栏是否应用主题色
const topHeaderThemeColorOpen = ref(getCacheConfig('SNOWY_TOP_HEADER_THEME_COLOR_OPEN'))
// 顶栏主题色通栏
const topHeaderThemeColorSpread = ref(getCacheConfig('SNOWY_TOP_HEADER_THEME_COLOR_SPREAD'))
// 模块坞
const moduleUnfoldOpen = ref(getCacheConfig('SNOWY_MODULE_UNFOLD_OPEN'))
// 主题
const theme = ref(getCacheConfig('SNOWY_THEME'))
// 主题颜色
const themeColor = ref(toolDataGet('SNOWY_THEME_COLOR') || config.COLOR)
// 整体表单风格
const formStyle = ref(getCacheConfig('SNOWY_FORM_STYLE'))
// 用户信息
const userInfo = ref(toolDataGet('USER_INFO') || {})
// 系统配置
const sysBaseConfig = ref(toolDataGet('SNOWY_SYS_BASE_CONFIG') || config.SYS_BASE_CONFIG)
// 默认应用
const module = ref(getCacheConfig('SNOWY_MENU_MODULE_ID'))
// 定义action
const setIsMobile = (key) => {
isMobile.value = key
}
const setLayout = (key) => {
layout.value = key
}
const setTheme = (key) => {
theme.value = key
const closeMessage = message.loading(`加载中...`)
changeColor(themeColor.value, key).then(closeMessage)
}
const setThemeColor = (key) => {
themeColor.value = key
const closeMessage = message.loading(`加载中...`)
changeColor(key, theme.value).then(closeMessage)
}
const initTheme = () => {
const closeMessage = message.loading(`加载中...`)
changeColor(themeColor.value, theme.value).then(closeMessage)
}
const toggleConfig = (key) => {
switch (key) {
case 'menuIsCollapse':
menuIsCollapse.value = !menuIsCollapse.value
break
case 'topHeaderThemeColorSpread':
topHeaderThemeColorSpread.value = !topHeaderThemeColorSpread.value
break
case 'sideUniqueOpen':
sideUniqueOpen.value = !sideUniqueOpen.value
break
case 'layoutTagsOpen':
layoutTagsOpen.value = !layoutTagsOpen.value
break
case 'breadcrumbOpen':
breadcrumbOpen.value = !breadcrumbOpen.value
break
case 'topHeaderThemeColorOpen':
topHeaderThemeColorOpen.value = !topHeaderThemeColorOpen.value
topHeaderThemeColorSpread.value = topHeaderThemeColorOpen.value
? topHeaderThemeColorSpread.value
: topHeaderThemeColorOpen.value
break
case 'moduleUnfoldOpen':
moduleUnfoldOpen.value = !moduleUnfoldOpen.value
break
}
}
const setFormStyle = (key) => {
formStyle.value = key
}
const setUserInfo = (key) => {
userInfo.value = key
}
const setSysBaseConfig = (key) => {
sysBaseConfig.value = key
}
const setModule = (key) => {
module.value = key
}
return {
isMobile,
layout,
menuIsCollapse,
sideUniqueOpen,
layoutTagsOpen,
breadcrumbOpen,
topHeaderThemeColorOpen,
topHeaderThemeColorSpread,
moduleUnfoldOpen,
theme,
themeColor,
formStyle,
userInfo,
sysBaseConfig,
module,
setIsMobile,
setLayout,
setTheme,
setThemeColor,
initTheme,
toggleConfig,
setFormStyle,
setUserInfo,
setSysBaseConfig,
setModule
}
})
export const useGlobalStore = globalStore

View File

@ -10,43 +10,49 @@
*/
import { defineStore } from 'pinia'
export const iframeStore = defineStore({
id: 'iframe',
state: () => ({
iframeList: []
}),
getters: {},
actions: {
setIframeList(route) {
this.iframeList = []
this.iframeList.push(route)
},
pushIframeList(route) {
const target = this.iframeList.find((item) => item.path === route.path)
if (!target) {
this.iframeList.push(route)
}
},
removeIframeList(route) {
this.iframeList.forEach((item, index) => {
if (item.path === route.path) {
this.iframeList.splice(index, 1)
}
})
},
refreshIframe(route) {
this.iframeList.forEach((item) => {
if (item.path === route.path) {
const url = route.meta.url
item.meta.url = ''
setTimeout(() => {
item.meta.url = url
}, 200)
}
})
},
clearIframeList() {
this.iframeList = []
export const iframeStore = defineStore('iframe', () => {
// 定义state
const iframeList = ref([])
const setIframeList = (route) => {
iframeList.value = []
iframeList.value.push(route)
}
// 定义action
const pushIframeList = (route) => {
const target = iframeList.value.find((item) => item.path === route.path)
if (!target) {
iframeList.value.push(route)
}
}
const removeIframeList = (route) => {
iframeList.value.forEach((item, index) => {
if (item.path === route.path) {
iframeList.value.splice(index, 1)
}
})
}
const refreshIframe = (route) => {
iframeList.value.forEach((item) => {
if (item.path === route.path) {
const url = route.meta.url
item.meta.url = ''
setTimeout(() => {
item.meta.url = url
}, 200)
}
})
}
const clearIframeList = () => {
iframeList.value = []
}
return {
iframeList,
setIframeList,
pushIframeList,
removeIframeList,
refreshIframe,
clearIframeList
}
})

View File

@ -10,37 +10,46 @@
*/
import { defineStore } from 'pinia'
export const keepAliveStore = defineStore({
id: 'keepAlive',
state: () => ({
keepLiveRoute: [],
routeKey: null,
routeShow: true
}),
getters: {},
actions: {
pushKeepLive(component) {
if (!this.keepLiveRoute.includes(component)) {
this.keepLiveRoute.push(component)
}
},
removeKeepLive(component) {
const index = this.keepLiveRoute.indexOf(component)
if (index !== -1) {
this.keepLiveRoute.splice(index, 1)
}
},
clearKeepLive() {
this.keepLiveRoute = []
},
setRouteKey(key) {
this.routeKey = key
},
setRouteShow(key) {
this.routeShow = key
},
setRouteKeyAction(key) {
this.setRouteKey(key)
export const keepAliveStore = defineStore('keepAlive', () => {
// 定义state
const keepLiveRoute = ref([])
const routeKey = ref(null)
const routeShow = ref(true)
// 定义action
const pushKeepLive = (component) => {
if (!keepLiveRoute.value.includes(component)) {
keepLiveRoute.value.push(component)
}
}
const removeKeepLive = (component) => {
const index = keepLiveRoute.value.indexOf(component)
if (index !== -1) {
keepLiveRoute.value.splice(index, 1)
}
}
const clearKeepLive = () => {
keepLiveRoute.value = []
}
const setRouteKey = (key) => {
routeKey.value = key
}
const setRouteShow = (key) => {
routeShow.value = key
}
const setRouteKeyAction = (key) => {
setRouteKey(key)
}
return {
keepLiveRoute,
routeKey,
routeShow,
pushKeepLive,
removeKeepLive,
clearKeepLive,
setRouteKey,
setRouteShow,
setRouteKeyAction
}
})

View File

@ -1,57 +1,63 @@
import '@/utils/objects'
import { defineStore } from 'pinia'
export const searchStore = defineStore({
id: 'search',
state: () => ({
active: false,
hotkey: {
open: 's',
close: 'esc'
},
pool: []
}),
getters: {},
actions: {
toggleActive() {
this.active = !this.active
},
setActive(active) {
this.active = active
},
init(menu) {
const pool = []
const getFullName = function (meta) {
if (meta.breadcrumb) {
let list = []
meta.breadcrumb.forEach((item) => {
list.push(item.meta.title)
})
return list.join(' / ')
}
return meta.title
}
const push = function (menu) {
menu.forEach((m) => {
if ('menu' === m.meta.type) {
if (m.children) {
push(m.children)
} else if (m.children === null) {
pool.push({
icon: m.meta.icon,
path: m.path,
fullPath: m.path,
name: m.meta.title,
fullName: getFullName(m.meta),
namePinyin: m.meta.title.toPinyin(),
namePinyinFirst: m.meta.title.toPinyin(true)
})
}
}
export const searchStore = defineStore('search', () => {
// 定义state
const pool = ref([])
const hotkey = ref({
open: 's',
close: 'esc'
})
const active = ref(false)
// 定义action
const toggleActive = () => {
active.value = !active.value
}
const setActive = (val) => {
active.value = val
}
const init = (menu) => {
const poolList = []
const getFullName = function (meta) {
if (meta.breadcrumb) {
let list = []
meta.breadcrumb.forEach((item) => {
list.push(item.meta.title)
})
return list.join(' / ')
}
push(menu)
this.pool = pool
return meta.title
}
const push = function (menu) {
menu.forEach((m) => {
if ('menu' === m.meta.type) {
if (m.children) {
push(m.children)
} else if (m.children === null) {
poolList.push({
icon: m.meta.icon,
path: m.path,
fullPath: m.path,
name: m.meta.title,
fullName: getFullName(m.meta),
namePinyin: m.meta.title.toPinyin(),
namePinyinFirst: m.meta.title.toPinyin(true)
})
}
}
})
}
push(menu)
pool.value = poolList
}
return {
pool,
hotkey,
active,
toggleActive,
setActive,
init
}
})

View File

@ -10,51 +10,59 @@
*/
import { defineStore } from 'pinia'
export const viewTagsStore = defineStore({
id: 'viewTags',
state: () => ({
viewTags: []
}),
getters: {},
actions: {
pushViewTags(route) {
const target = this.viewTags.find((item) => item.path === route.path)
const isName = route.name
if (!target && isName) {
this.viewTags.push(route)
}
if (target) {
this.viewTags.forEach((item) => {
if (item.path === route.path) {
Object.assign(item, route)
}
})
}
},
removeViewTags(route) {
this.viewTags.forEach((item, index) => {
if (item.fullPath === route.fullPath) {
this.viewTags.splice(index, 1)
export const viewTagsStore = defineStore('viewTags', () => {
// 定义state
const viewTags = ref([])
// 定义action
const pushViewTags = (route) => {
const target = viewTags.value.find((item) => item.path === route.path)
const isName = route.name
if (!target && isName) {
viewTags.value.push(route)
}
if (target) {
viewTags.value.forEach((item, index) => {
if (item.path === route.path) {
viewTags.value[index] = { ...route, ...item }
// Object.assign(item, route)
}
})
},
updateViewTags(route) {
this.viewTags.forEach((item) => {
if (item.fullPath == route.fullPath) {
Object.assign(item, route)
}
})
},
updateViewTagsTitle(title = '') {
const nowFullPath = location.hash.substring(1)
this.viewTags.forEach((item) => {
if (item.fullPath == nowFullPath) {
item.meta.title = title
}
})
},
clearViewTags() {
this.viewTags = []
}
}
const removeViewTags = (route) => {
viewTags.value.forEach((item, index) => {
if (item.fullPath === route.fullPath) {
viewTags.value.splice(index, 1)
}
})
}
const updateViewTags = (route) => {
viewTags.value.forEach((item, index) => {
if (item.fullPath === route.fullPath) {
viewTags.value[index] = { ...route, ...item }
// Object.assign(item, route)
}
})
}
const updateViewTagsTitle = (title = '') => {
const nowFullPath = location.hash.substring(1)
viewTags.value.forEach((item) => {
if (item.fullPath === nowFullPath) {
item.meta.title = title
}
})
}
const clearViewTags = () => {
viewTags.value = []
}
return {
viewTags,
pushViewTags,
removeViewTags,
updateViewTags,
updateViewTagsTitle,
clearViewTags
}
})

View File

@ -21,7 +21,7 @@ export default {
RgbToHex(a, b, c) {
const hexs = [a.toString(16), b.toString(16), c.toString(16)]
for (let i = 0; i < 3; i++) {
if (hexs[i].length == 1) hexs[i] = `0${hexs[i]}`
if (hexs[i].length === 1) hexs[i] = `0${hexs[i]}`
}
return `#${hexs.join('')}`
},

View File

@ -57,13 +57,11 @@ export const watermark = {
if (wmInstance) {
// 避免一直触发
// observer.disconnect();
// console.log('水印属性修改了');
wmInstance.setAttribute('style', styleStr)
} else {
/* 此处根据用户登录状态,判断是否终止监听,避免用户退出后登录页面仍然有水印 */
if (tool.data.get('TOKEN')) {
//标签被移除,重新添加标签
// console.log('水印标签被移除了');
document.body.appendChild(watermark)
} else {
observer.disconnect()

View File

@ -26,7 +26,6 @@
import loginApi from '@/api/auth/loginApi'
import userCenterApi from '@/api/sys/userCenterApi'
import dictApi from '@/api/dev/dictApi'
import { onMounted } from 'vue'
onMounted(() => {
// url

View File

@ -97,140 +97,145 @@
</div>
</div>
</template>
<script>
<script setup>
import loginApi from '@/api/auth/loginApi'
import phoneLoginForm from './phoneLoginForm.vue'
import threeLogin from './threeLogin.vue'
import PhoneLoginForm from './phoneLoginForm.vue'
import ThreeLogin from './threeLogin.vue'
import smCrypto from '@/utils/smCrypto'
import { required } from '@/utils/formRules'
import { afterLogin } from './util'
import config from '@/config'
import configData from '@/config'
import configApi from '@/api/dev/configApi'
import tool from '@/utils/tool'
import { globalStore, iframeStore, keepAliveStore, viewTagsStore } from '@/store'
import { mapActions, mapState } from 'pinia'
const { proxy } = getCurrentInstance()
export default {
name: 'Login',
components: {
phoneLoginForm,
threeLogin
const activeKey = ref('userAccount')
const captchaOpen = ref(configData.SYS_BASE_CONFIG.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN)
const validCodeBase64 = ref('')
const loading = ref(false)
const ruleForm = reactive({
account: 'superAdmin',
password: '123456',
validCode: '',
validCodeReqNo: '',
autologin: false
})
const rules = reactive({
account: [required(proxy.$t('login.accountError'), 'blur')],
password: [required(proxy.$t('login.PWError'), 'blur')]
})
const lang = ref([
{
name: '简体中文',
value: 'zh-cn'
},
data() {
return {
activeKey: 'userAccount',
captchaOpen: config.SYS_BASE_CONFIG.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN,
validCodeBase64: '',
ruleForm: {
account: 'superAdmin',
password: '123456',
validCode: '',
validCodeReqNo: '',
autologin: false
},
rules: {
account: [required(this.$t('login.accountError'), 'blur')],
password: [required(this.$t('login.PWError'), 'blur')]
},
loading: false,
config: {
lang: tool.data.get('APP_LANG') || this.$CONFIG.LANG,
theme: tool.data.get('APP_THEME') || 'default'
},
lang: [
{
name: '简体中文',
value: 'zh-cn'
},
{
name: 'English',
value: 'en'
}
]
}
},
computed: {
...mapState(globalStore, ['sysBaseConfig']),
},
watch: {
'config.theme': function (val) {
document.body.setAttribute('data-theme', val)
},
'config.lang': function (val) {
this.$i18n.locale = val
tool.data.set('APP_LANG', val)
}
},
created() {
this.clearViewTags()
this.clearKeepLive()
this.clearIframeList()
},
mounted() {
let formData = ref(config.SYS_BASE_CONFIG)
configApi.configSysBaseList().then((data) => {
if (data) {
data.forEach((item) => {
formData.value[item.configKey] = item.configValue
})
this.captchaOpen = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
this.setSysBaseConfig(formData.value)
this.refreshSwitch()
}
})
},
methods: {
...mapActions(keepAliveStore, ['clearKeepLive']),
...mapActions(viewTagsStore, ['clearViewTags']),
...mapActions(iframeStore, ['clearIframeList']),
...mapActions(globalStore, ['setSysBaseConfig']),
//
refreshSwitch() {
//
if (this.captchaOpen === 'true') {
//
this.loginCaptcha()
//
this.rules.validCode = [required(this.$t('login.validError'), 'blur')]
}
},
//
loginCaptcha() {
loginApi.getPicCaptcha().then((data) => {
this.validCodeBase64 = data.validCodeBase64
this.ruleForm.validCodeReqNo = data.validCodeReqNo
{
name: 'English',
value: 'en'
}
])
const config = ref({
lang: tool.data.get('APP_LANG') || proxy.$CONFIG.LANG,
theme: tool.data.get('APP_THEME') || 'default'
})
const store = globalStore()
const kStore = keepAliveStore()
const iStore = iframeStore()
const vStore = viewTagsStore()
const setSysBaseConfig = store.setSysBaseConfig
const clearKeepLive = kStore.clearKeepLive
const clearIframeList = iStore.clearIframeList
const clearViewTags = vStore.clearViewTags
const sysBaseConfig = computed(() => {
return store.sysBaseConfig
})
onMounted(() => {
let formData = ref(configData.SYS_BASE_CONFIG)
configApi.configSysBaseList().then((data) => {
if (data) {
data.forEach((item) => {
formData.value[item.configKey] = item.configValue
})
},
//
async login() {
this.$refs.loginForm.validate().then(async () => {
this.loading = true
const loginData = {
account: this.ruleForm.account,
// SM2使hash
password: smCrypto.doSm2Encrypt(this.ruleForm.password),
validCode: this.ruleForm.validCode,
validCodeReqNo: this.ruleForm.validCodeReqNo
}
// token
try {
const loginToken = await loginApi.login(loginData)
afterLogin(loginToken)
} catch (err) {
this.loading = false
this.loginCaptcha()
}
})
},
configLang(key) {
this.config.lang = key
captchaOpen.value = formData.value.SNOWY_SYS_DEFAULT_CAPTCHA_OPEN
tool.data.set('SNOWY_SYS_BASE_CONFIG', formData.value)
setSysBaseConfig(formData.value)
refreshSwitch()
}
})
})
onBeforeMount(() => {
clearViewTags()
clearKeepLive()
clearIframeList()
})
//
watch(
() => config.value.lang,
(newValue) => {
proxy.$i18n.locale = newValue
tool.data.set('APP_LANG', newValue)
}
)
//
watch(
() => config.value.theme,
(newValue) => {
document.body.setAttribute('data-theme', newValue)
}
)
//
const refreshSwitch = () => {
//
if (captchaOpen.value === 'true') {
//
loginCaptcha()
//
rules.validCode = [required(proxy.$t('login.validError'), 'blur')]
}
}
</script>
//
const loginCaptcha = () => {
loginApi.getPicCaptcha().then((data) => {
validCodeBase64.value = data.validCodeBase64
ruleForm.validCodeReqNo = data.validCodeReqNo
})
}
//
const loginForm = ref()
const login = async () => {
loginForm.value.validate().then(async () => {
loading.value = true
const loginData = {
account: ruleForm.account,
// SM2使hash
password: smCrypto.doSm2Encrypt(ruleForm.password),
validCode: ruleForm.validCode,
validCodeReqNo: ruleForm.validCodeReqNo
}
// token
try {
const loginToken = await loginApi.login(loginData)
afterLogin(loginToken)
} catch (err) {
loading.value = false
loginCaptcha()
}
})
}
const configLang = (key) => {
config.value.lang = key
}
</script>
<style lang="less">
@import 'login';
</style>

View File

@ -22,7 +22,7 @@ export const afterLogin = async (loginToken) => {
// 重置系统默认应用
tool.data.set('SNOWY_MENU_MODULE_ID', menu[0].id)
message.success('登录成功')
if (!!tool.data.get('LAST_VIEWS_PATH')) {
if (tool.data.get('LAST_VIEWS_PATH')) {
// 如果有缓存,将其登录跳转到最后访问的路由
indexMenu = tool.data.get('LAST_VIEWS_PATH')
}

View File

@ -1,5 +1,5 @@
<template>
<s-table ref="table" :columns="columns" :data="loadDataB" :alert="false" bordered :row-key="(record) => record.id">
<s-table ref="tableRef" :columns="columns" :data="loadDataB" :alert="false" bordered :row-key="(record) => record.id">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'avatar'">
<a-avatar :src="record.avatar" style="width: 25px; height: 25px" />
@ -18,13 +18,13 @@
</template>
</template>
</s-table>
<token-info-list ref="tokenInfoList" @successful="table.refresh()" />
<token-info-list ref="tokenInfoList" @successful="tableRef.refresh()" />
</template>
<script setup name="monitorBTab">
import monitorApi from '@/api/auth/monitorApi'
import TokenInfoList from './tokenInfoList.vue'
const table = ref(null)
const tableRef = ref(null)
const tokenInfoList = ref()
const columns = [
{
@ -83,7 +83,7 @@
}
]
monitorApi.monitorBExit(params).then(() => {
table.value.refresh(true)
tableRef.value.refresh(true)
})
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<s-table ref="table" :columns="columns" :data="loadDataC" :alert="false" bordered :row-key="(record) => record.id">
<s-table ref="tableRef" :columns="columns" :data="loadDataC" :alert="false" bordered :row-key="(record) => record.id">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'avatar'">
<a-avatar :src="record.avatar" style="width: 25px; height: 25px" />
@ -18,13 +18,13 @@
</template>
</template>
</s-table>
<token-info-list ref="tokenInfoList" @successful="table.refresh()" />
<token-info-list ref="tokenInfoList" @successful="tableRef.refresh()" />
</template>
<script setup name="monitorCTab">
import monitorApi from '@/api/auth/monitorApi'
import TokenInfoList from './tokenInfoList.vue'
const table = ref(null)
const tableRef = ref(null)
const tokenInfoList = ref()
const columns = [
{
@ -83,7 +83,7 @@
}
]
monitorApi.monitorCExit(params).then(() => {
table.value.refresh(true)
tableRef.value.refresh(true)
})
}
</script>

View File

@ -13,8 +13,8 @@
</template>
<script setup name="authMonitor">
import analyse from './analyse.vue'
import monitorBTab from './bTab.vue'
import monitorCTab from './cTab.vue'
import Analyse from './analyse.vue'
import MonitorBTab from './bTab.vue'
import MonitorCTab from './cTab.vue'
const activeKey = ref('1')
</script>

View File

@ -1,11 +1,5 @@
<template>
<xn-form-container
title="令牌列表"
:width="650"
:visible="visible"
:destroy-on-close="true"
@close="onClose"
>
<xn-form-container title="令牌列表" :width="650" :visible="visible" :destroy-on-close="true" @close="onClose">
<a-button
danger
style="margin-bottom: 10px"
@ -104,7 +98,7 @@
//
const loadData = ref([])
//
let visible = $ref(false)
const visible = ref(false)
//
const selectedRowKeys = ref([])
const exitLoading = ref(false)
@ -116,13 +110,13 @@
const onOpen = (tokenInfoList, type) => {
monitorType.value = type
loadData.value = cloneDeep(tokenInfoList)
visible = true
visible.value = true
}
//
const onClose = () => {
loadData.value = []
monitorType.value = ''
visible = false
visible.value = false
}
//
const rowSelection = {

View File

@ -14,7 +14,7 @@
</a-form-item>
</a-col>
<a-col :span="6">
<a-button type="primary" @click="table.refresh(true)"></a-button>
<a-button type="primary" @click="tableRef.refresh(true)"></a-button>
<a-button style="margin: 0 8px" @click="reset"></a-button>
</a-col>
</a-row>
@ -22,7 +22,7 @@
</a-card>
<a-card :bordered="false">
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:alert="false"
@ -45,9 +45,9 @@
<script setup name="authThird">
import thirdApi from '@/api/auth/thirdApi'
import tool from '@/utils/tool'
let searchFormState = reactive({})
const searchFormState = ref({})
const searchFormRef = ref()
const table = ref()
const tableRef = ref()
const toolConfig = { refresh: true, height: true, columnSetting: false, striped: false }
const columns = [
{
@ -83,14 +83,14 @@
}
]
const loadData = (parameter) => {
return thirdApi.thirdPage(Object.assign(parameter, searchFormState)).then((res) => {
return thirdApi.thirdPage(Object.assign(parameter, searchFormState.value)).then((res) => {
return res
})
}
//
const reset = () => {
searchFormRef.value.resetFields();
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const categoryOptions = tool.dictList('THIRD_CATEGORY')

View File

@ -27,7 +27,7 @@
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="table.refresh(true)">
<a-button type="primary" @click="tableRef.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
@ -41,7 +41,7 @@
</a-card>
<a-card :bordered="false" style="margin-bottom: 10px">
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
@ -95,7 +95,7 @@
})
}
// tableDOM
const table = ref(null)
const tableRef = ref(null)
const formRef = ref()
const cardLoading = ref(true)
const searchFormRef = ref()
@ -136,7 +136,7 @@
//
const reset = () => {
searchFormRef.value.resetFields()
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const loadTreeData = () => {
@ -166,11 +166,11 @@
delete searchFormState.value.parentId
columns.splice(2, 1)
}
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const formSuccessful = () => {
table.value.refresh()
tableRef.value.refresh()
refreshStoreDict()
}
// store

View File

@ -72,7 +72,7 @@
// emit
const emit = defineEmits({ successful: null })
//
let visible = $ref(false)
const visible = ref(false)
let UserSelectorPlus = ref()
const formRef = ref()
//
@ -83,7 +83,7 @@
//
const onOpen = (record, parentId) => {
visible = true
visible.value = true
formData.value = {
sortCode: 99
}
@ -112,7 +112,7 @@
}
//
const onClose = () => {
visible = false
visible.value = false
}
//
const formRules = {
@ -152,7 +152,7 @@
bizOrgApi
.submitForm(formData.value, formData.value.id)
.then(() => {
visible = false
visible.value = false
emit('successful')
})
.finally(() => {

View File

@ -23,7 +23,7 @@
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="table.refresh(true)">
<a-button type="primary" @click="tableRef.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
@ -37,7 +37,7 @@
</a-card>
<a-card :bordered="false">
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
@ -51,7 +51,7 @@
<a-space>
<a-button
type="primary"
@click="form.onOpen(undefined, searchFormState.parentId)"
@click="formRef.onOpen(undefined, searchFormState.parentId)"
v-if="hasPerm('bizOrgAdd')"
>
<template #icon><plus-outlined /></template>
@ -69,7 +69,7 @@
{{ $TOOL.dictTypeData('ORG_CATEGORY', record.category) }}
</template>
<template v-if="column.dataIndex === 'action'">
<a @click="form.onOpen(record)" v-if="hasPerm('bizOrgEdit')"></a>
<a @click="formRef.onOpen(record)" v-if="hasPerm('bizOrgEdit')"></a>
<a-divider type="vertical" v-if="hasPerm(['bizOrgEdit', 'bizOrgDelete'], 'and')" />
<a-popconfirm title="删除此机构与下级机构吗?" @confirm="removeOrg(record)">
<a-button type="link" danger size="small" v-if="hasPerm('bizOrgDelete')"></a-button>
@ -80,7 +80,7 @@
</a-card>
</a-col>
</a-row>
<Form ref="form" @successful="table.refresh()" />
<Form ref="formRef" @successful="tableRef.refresh()" />
</template>
<script setup name="bizOrg">
@ -129,8 +129,8 @@
}
const toolConfig = { refresh: true, height: true, columnSetting: true }
// tableDOM
const table = ref(null)
const form = ref()
const tableRef = ref(null)
const formRef = ref()
const searchFormRef = ref()
const searchFormState = ref({})
//
@ -150,7 +150,7 @@
//
const reset = () => {
searchFormRef.value.resetFields()
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const loadTreeData = () => {
@ -188,7 +188,7 @@
} else {
delete searchFormState.value.parentId
}
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const removeOrg = (record) => {
@ -198,13 +198,13 @@
}
]
bizOrgApi.orgDelete(params).then(() => {
table.value.refresh(true)
tableRef.value.refresh(true)
})
}
//
const deleteBatchOrg = (params) => {
bizOrgApi.orgDelete(params).then(() => {
table.value.clearRefreshSelected()
tableRef.value.clearRefreshSelected()
})
}
</script>

View File

@ -23,7 +23,7 @@
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="table.refresh(true)">
<a-button type="primary" @click="tableRef.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
@ -37,7 +37,7 @@
</a-card>
<a-card :bordered="false">
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
@ -80,7 +80,7 @@
</a-card>
</a-col>
</a-row>
<Form ref="formRef" @successful="table.refresh(true)" />
<Form ref="formRef" @successful="tableRef.refresh(true)" />
</template>
<script setup name="bizPosition">
@ -129,7 +129,7 @@
}
const toolConfig = { refresh: true, height: true, columnSetting: true }
// tableDOM
const table = ref(null)
const tableRef = ref(null)
const formRef = ref()
const searchFormRef = ref()
const searchFormState = ref({})
@ -149,7 +149,7 @@
//
const reset = () => {
searchFormRef.value.resetFields()
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
bizOrgApi
@ -185,7 +185,7 @@
} else {
delete searchFormState.value.orgId
}
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const removeOrg = (record) => {
@ -195,13 +195,13 @@
}
]
bizPositionApi.positionDelete(params).then(() => {
table.value.refresh(true)
tableRef.value.refresh(true)
})
}
//
const deleteBatchPosition = (params) => {
bizPositionApi.positionDelete(params).then(() => {
table.value.clearRefreshSelected()
tableRef.value.clearRefreshSelected()
})
}
</script>

View File

@ -35,7 +35,7 @@
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="table.refresh(true)">
<a-button type="primary" @click="tableRef.refresh(true)">
<template #icon><SearchOutlined /></template>
{{ $t('common.searchButton') }}
</a-button>
@ -49,7 +49,7 @@
</a-card>
<a-card :bordered="false">
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
@ -140,7 +140,7 @@
</a-card>
</a-col>
</a-row>
<Form ref="formRef" @successful="table.refresh()" />
<Form ref="formRef" @successful="tableRef.refresh()" />
<role-selector-plus
ref="RoleSelectorPlusRef"
:org-tree-api="selectorApiFunction.orgTreeApi"
@ -216,7 +216,7 @@
const searchFormRef = ref()
const defaultExpandedKeys = ref([])
const searchFormState = ref({})
const table = ref(null)
const tableRef = ref(null)
const treeData = ref([])
const selectedRowKeys = ref([])
const treeFieldNames = { children: 'children', title: 'name', key: 'id' }
@ -234,7 +234,7 @@
//
const reset = () => {
searchFormRef.value.resetFields()
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
bizOrgApi
@ -284,7 +284,7 @@
} else {
delete searchFormState.value.orgId
}
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const editStatus = (record) => {
@ -293,7 +293,7 @@
bizUserApi
.userDisableUser(record)
.then(() => {
table.value.refresh()
tableRef.value.refresh()
})
.finally(() => {
loading.value = false
@ -302,7 +302,7 @@
bizUserApi
.userEnableUser(record)
.then(() => {
table.value.refresh()
tableRef.value.refresh()
})
.finally(() => {
loading.value = false
@ -317,7 +317,7 @@
}
]
bizUserApi.userDelete(params).then(() => {
table.value.refresh()
tableRef.value.refresh()
})
}
//
@ -348,13 +348,13 @@
const exportBatchUser = (params) => {
bizUserApi.userExport(params).then((res) => {
downloadUtil.resultDownload(res)
table.value.clearSelected()
tableRef.value.clearSelected()
})
}
//
const deleteBatchUser = (params) => {
bizUserApi.userDelete(params).then(() => {
table.value.clearRefreshSelected()
tableRef.value.clearRefreshSelected()
})
}
//

View File

@ -13,8 +13,8 @@
</template>
<script setup name="emailConfig">
import localEmailForm from './localEmailForm.vue'
import aliyunEmailForm from './aliyunEmailForm.vue'
import tencentEmailForm from './tencentEmailForm.vue'
import LocalEmailForm from './localEmailForm.vue'
import AliyunEmailForm from './aliyunEmailForm.vue'
import TencentEmailForm from './tencentEmailForm.vue'
const activeKey = ref('localEmail')
</script>

View File

@ -16,9 +16,9 @@
</template>
<script setup name="fileConfig">
import localFileForm from './localFileForm.vue'
import aliyunFileForm from './aliyunFileForm.vue'
import tencentFileForm from './tencentFileForm.vue'
import minioFileForm from './minioFileForm.vue'
import LocalFileForm from './localFileForm.vue'
import AliyunFileForm from './aliyunFileForm.vue'
import TencentFileForm from './tencentFileForm.vue'
import MinioFileForm from './minioFileForm.vue'
const activeKey = ref('localFile')
</script>

View File

@ -35,7 +35,7 @@
import { required } from '@/utils/formRules'
import configApi from '@/api/dev/configApi'
//
let visible = $ref(false)
const visible = ref(false)
const emit = defineEmits({ successful: null })
const formRef = ref()
//
@ -44,7 +44,7 @@
//
const onOpen = (record) => {
visible = true
visible.value = true
formData.value = {
sortCode: 99
}
@ -55,7 +55,7 @@
//
const onClose = () => {
formRef.value.resetFields()
visible = false
visible.value = false
}
//
const formRules = {

View File

@ -1,6 +1,6 @@
<template>
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:alert="false"
@ -10,7 +10,7 @@
>
<template #operator class="table-operator">
<a-space>
<a-button type="primary" @click="form.onOpen()">
<a-button type="primary" @click="formRef.onOpen()">
<template #icon>
<plus-outlined />
</template>
@ -21,14 +21,14 @@
placeholder="请输入关键字"
enter-button
allowClear
@search="table.refresh(true)"
@search="tableRef.refresh(true)"
/>
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'action'">
<a-space>
<a @click="form.onOpen(record)"></a>
<a @click="formRef.onOpen(record)"></a>
<a-divider type="vertical" />
<a-popconfirm title="确定要删除此配置吗?" @confirm="deleteConfig(record)">
<a-button type="link" danger size="small">删除</a-button>
@ -37,16 +37,15 @@
</template>
</template>
</s-table>
<Form ref="form" @successful="table.refresh(true)" />
<Form ref="formRef" @successful="tableRef.refresh(true)" />
</template>
<script setup name="sysModule">
import Form from './form.vue'
import configApi from '@/api/dev/configApi'
let searchFormState = reactive({})
const searchFormState = ref({})
const formRef = ref()
const table = ref()
let form = ref()
const tableRef = ref()
const toolConfig = { refresh: true, height: true, columnSetting: false, striped: false }
const columns = [
{
@ -77,7 +76,7 @@
}
]
const loadData = (parameter) => {
return configApi.configPage(Object.assign(parameter, searchFormState)).then((res) => {
return configApi.configPage(Object.assign(parameter, searchFormState.value)).then((res) => {
return res
})
}
@ -89,7 +88,7 @@
}
]
configApi.configDelete(params).then(() => {
table.value.refresh(true)
tableRef.value.refresh(true)
})
}
</script>

View File

@ -10,7 +10,7 @@
</template>
<script setup name="smsConfig">
import aliyunSmsForm from './aliyunSmsForm.vue'
import tencentSmsForm from './tencentSmsForm.vue'
import AliyunSmsForm from './aliyunSmsForm.vue'
import TencentSmsForm from './tencentSmsForm.vue'
const activeKey = ref('aliyunSms')
</script>

View File

@ -102,7 +102,7 @@
import { message } from 'ant-design-vue'
import configApi from '@/api/dev/configApi'
import tool from '@/utils/tool'
import menuTreeSelect from '@/components/TreeSelect/menuTreeSelect.vue'
import MenuTreeSelect from '@/components/TreeSelect/menuTreeSelect.vue'
// emit
const emit = defineEmits({ successful: null })

View File

@ -10,7 +10,7 @@
</template>
<script setup name="thirdConfig">
import wechatThirdForm from './wechatThirdForm.vue'
import giteeThirdForm from './giteeThirdForm.vue'
import WechatThirdForm from './wechatThirdForm.vue'
import GiteeThirdForm from './giteeThirdForm.vue'
const activeKey = ref('wechatThird')
</script>

View File

@ -20,7 +20,7 @@
</a-form-item>
</a-col>
<a-col :span="8">
<a-button type="primary" @click="$refs.table.refresh(true)">
<a-button type="primary" @click="tableRef.refresh(true)">
<template #icon><SearchOutlined /></template>
查询
</a-button>
@ -33,7 +33,7 @@
</a-form>
<a-divider class="m-3 mx-0" />
<s-table
ref="table"
ref="tableRef"
:columns="columns"
:data="loadData"
:expand-row-by-click="true"
@ -42,7 +42,7 @@
:row-key="(record) => record.id"
>
<template #operator class="table-operator">
<a-button type="primary" @click="form.onOpen(undefined, 'BIZ', searchFormState.parentId)">
<a-button type="primary" @click="formRef.onOpen(undefined, 'BIZ', searchFormState.parentId)">
<template #icon><plus-outlined /></template>
新增
</a-button>
@ -53,7 +53,7 @@
<a-tag color="green" v-else></a-tag>
</template>
<template v-if="column.dataIndex === 'action'">
<a @click="form.onOpen(record, 'BIZ')">编辑</a>
<a @click="formRef.onOpen(record, 'BIZ')">编辑</a>
<a-divider type="vertical" />
<a-popconfirm title="删除此字典与下级字典吗?" @confirm="remove(record)">
<a-button type="link" danger size="small">删除</a-button>
@ -63,14 +63,14 @@
</s-table>
</a-col>
</a-row>
<Form ref="form" @successful="formSuccessful()" />
<Form ref="formRef" @successful="formSuccessful()" />
</template>
<script setup>
import { Empty } from 'ant-design-vue'
import dictApi from '@/api/dev/dictApi'
import Form from './form.vue'
const { proxy } = getCurrentInstance()
import tool from '@/utils/tool'
const columns = [
{
title: '字典名称',
@ -94,10 +94,10 @@
}
]
// tableDOM
const table = ref(null)
const form = ref()
const tableRef = ref(null)
const formRef = ref()
const searchFormRef = ref()
let searchFormState = reactive({})
const searchFormState = ref({})
//
let defaultExpandedKeys = ref([])
const treeData = ref([])
@ -109,9 +109,9 @@
const loadData = (parameter) => {
loadTreeData()
parameter.category = 'BIZ'
return dictApi.dictPage(Object.assign(parameter, searchFormState)).then((data) => {
return dictApi.dictPage(Object.assign(parameter, searchFormState.value)).then((data) => {
if (data.records) {
if (searchFormState.parentId) {
if (searchFormState.value.parentId) {
let dataArray = []
data.records.forEach((item) => {
const obj = data.records.find((f) => f.id === item.parentId)
@ -135,7 +135,7 @@
//
const reset = () => {
searchFormRef.value.resetFields()
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const loadTreeData = () => {
@ -151,7 +151,7 @@
//
const treeSelect = (selectedKeys) => {
if (selectedKeys && selectedKeys.length > 0) {
searchFormState.parentId = selectedKeys.toString()
searchFormState.value.parentId = selectedKeys.toString()
if (!columns.find((f) => f.title === '层级')) {
columns.splice(2, 0, {
title: '层级',
@ -160,10 +160,10 @@
})
}
} else {
delete searchFormState.parentId
delete searchFormState.value.parentId
columns.splice(2, 1)
}
table.value.refresh(true)
tableRef.value.refresh(true)
}
//
const remove = (record) => {
@ -173,19 +173,19 @@
}
]
dictApi.dictDelete(params).then(() => {
table.value.refresh(true)
tableRef.value.refresh(true)
})
refreshStoreDict()
}
//
const formSuccessful = () => {
table.value.refresh()
tableRef.value.refresh()
refreshStoreDict()
}
// store
const refreshStoreDict = () => {
dictApi.dictTree().then((res) => {
proxy.$TOOL.data.set('DICT_TYPE_TREE_DATA', res)
tool.data.set('DICT_TYPE_TREE_DATA', res)
})
}
</script>

Some files were not shown because too many files have changed in this diff Show More