mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2025-11-12 05:33:48 +08:00
v3.14.0 更新;【新增】EasyExcel重磅升级为FastExcel;【新增】使用最强Argon2算法作为密码存储;【新增】大家吐槽的数据字典改为可重复;【新增】前端布局再增加多种样式;【优化】升级SaToken到最新版本;【优化】token使用Sa-Token的Bearer类型;【优化】优化其他
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in options">
|
||||
<template v-if="values.includes(item.valueCode)">
|
||||
{{ item.valueName }}
|
||||
<span> </span>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
options: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
// 当前的值
|
||||
value: [Number, String, Array],
|
||||
});
|
||||
const values = computed(() => {
|
||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '') return [];
|
||||
return Array.isArray(props.value) ? props.value.map((item) => item.valueCode) : props.value.split(',');
|
||||
});
|
||||
</script>
|
||||
@@ -99,10 +99,20 @@
|
||||
getHtml,
|
||||
getText,
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.w-e-full-screen-container {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
</style>
|
||||
<!-- 解决弹窗高度警告信息显示 -->
|
||||
<style>
|
||||
::v-deep .w-e-text-container {
|
||||
height: 420px !important;
|
||||
}
|
||||
.w-e-text-container .w-e-scroll {
|
||||
height: 500px !important;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<CopyOutlined
|
||||
@click="copy"
|
||||
:style="{
|
||||
color: `${color}`,
|
||||
}"
|
||||
class="icon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#1890ff',
|
||||
},
|
||||
});
|
||||
|
||||
function copy() {
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.value = props.value;
|
||||
document.body.appendChild(textarea);
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.body.removeChild(textarea);
|
||||
message.success('复制成功');
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.icon {
|
||||
margin: 0 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="table-title">
|
||||
<slot name="title">
|
||||
{{ column.title }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="filter" style="max-width: 300px; min-height: 40px">
|
||||
<slot>
|
||||
<template v-if="column.filterOptions">
|
||||
<template v-if="column.filterOptions.type === 'input'">
|
||||
<a-input-search
|
||||
@change="change('no-search')"
|
||||
v-model:value="modelValue"
|
||||
allowClear
|
||||
:placeholder="column.placeholder || column.title"
|
||||
@search="change"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.filterOptions.type === 'date-range'">
|
||||
<a-range-picker
|
||||
v-model:value="createDateRange"
|
||||
:ranges="column.filterOptions.ranges ? defaultTimeRanges : []"
|
||||
style="width: 100%"
|
||||
@change="changeCreateDate"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.filterOptions.type === 'datetime-range'">
|
||||
<a-range-picker
|
||||
show-time
|
||||
v-model:value="createDateRange"
|
||||
:ranges="column.filterOptions.ranges ? defaultTimeRanges : []"
|
||||
style="width: 100%"
|
||||
@change="changeCreateDate"
|
||||
/>
|
||||
</template>
|
||||
<template v-else-if="column.filterOptions.type === 'month'">
|
||||
<a-range-picker v-model:value="createDateRange" picker="month" @change="changeCreateDate" />
|
||||
</template>
|
||||
<template v-else-if="column.filterOptions.type === 'submit'">
|
||||
<div class="smart-table-operate"><a-button :type="column.filterOptions.btnType" @click="submit">查询</a-button></div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<component
|
||||
v-if="componentsKey.includes(column.filterOptions.type)"
|
||||
:is="getComponent(column.filterOptions.type)"
|
||||
width="100%"
|
||||
v-model:value="modelValue"
|
||||
:multiple="column.filterOptions.multiple"
|
||||
:keyCode="column.filterOptions.keyCode"
|
||||
:enumName="column.filterOptions.enumName"
|
||||
:systemConfigKey="column.filterOptions.systemConfigKey"
|
||||
:placeholder="column.placeholder"
|
||||
:leaveFlag="column.filterOptions.leaveFlag"
|
||||
:categoryType="column.filterOptions.categoryType"
|
||||
@change="change"
|
||||
/>
|
||||
<div v-else class="error-component">未定义的组件类型</div>
|
||||
</template>
|
||||
</template>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, useSlots, watch, onMounted, defineAsyncComponent } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import _ from 'lodash';
|
||||
import { defaultTimeRanges } from '/@/lib/default-time-ranges';
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: [Number, String, Array, Object],
|
||||
},
|
||||
column: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
|
||||
const components = {
|
||||
'enum-select': defineAsyncComponent(() => import('/@/components/framework/smart-enum-select/index.vue')),
|
||||
'dict-select': defineAsyncComponent(() => import('/@/components/support/dict-select/index.vue')),
|
||||
'employee-select': defineAsyncComponent(() => import('/@/components/system/employee-select/index.vue')),
|
||||
'enterprise-select': defineAsyncComponent(() => import('/@/components/business/oa/enterprise-select/index.vue')),
|
||||
'boolean-select': defineAsyncComponent(() => import('/@/components/framework/boolean-select/index.vue')),
|
||||
'category-tree': defineAsyncComponent(() => import('/@/components/business/category-tree-select/index.vue')),
|
||||
};
|
||||
|
||||
const componentsKey = Object.keys(components);
|
||||
|
||||
function getComponent(key) {
|
||||
return components[key];
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
() => {
|
||||
if (props.column.filterOptions && (props.column.filterOptions.type === 'date-range' || props.column.filterOptions.type === 'datetime-range')) {
|
||||
if (!_.isEmpty(props.value) && props.value.length === 2 && !_.isEmpty(props.value[0]) && !_.isEmpty(props.value[1])) {
|
||||
createDateRange.value = [dayjs(props.value[0]), dayjs(props.value[1])];
|
||||
} else {
|
||||
createDateRange.value = [];
|
||||
}
|
||||
} else {
|
||||
modelValue.value = props.value;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const slots = useSlots();
|
||||
|
||||
const emits = defineEmits(['change', 'update:value']);
|
||||
const createDateRange = ref([]);
|
||||
const modelValue = ref(undefined);
|
||||
|
||||
function changeCreateDate(dates, dateStrings) {
|
||||
// emits('update:value',dates)
|
||||
emits('change', {
|
||||
type: props.column.filterOptions.type,
|
||||
key: props.column.filterOptions.key || props.column.dataIndex,
|
||||
value: [dateStrings[0], dateStrings[1]],
|
||||
search: true,
|
||||
});
|
||||
}
|
||||
|
||||
//是否立即查询
|
||||
function change(search) {
|
||||
let isSearch = true;
|
||||
if (search === 'no-search') {
|
||||
isSearch = false;
|
||||
}
|
||||
emits('update:value', modelValue.value);
|
||||
emits('change', {
|
||||
type: props.column.filterOptions.type,
|
||||
key: props.column.filterOptions.key || props.column.dataIndex,
|
||||
value: modelValue.value,
|
||||
search: isSearch,
|
||||
});
|
||||
}
|
||||
|
||||
function submit() {
|
||||
emits('change', {
|
||||
search: true,
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.column.filterOptions && (props.column.filterOptions.type === 'date-range' || props.column.filterOptions.type === 'datetime-range')) {
|
||||
if (!_.isEmpty(props.value) && props.value.length === 2 && !_.isEmpty(props.value[0]) && !_.isEmpty(props.value[1])) {
|
||||
createDateRange.value = [dayjs(props.value[0]), dayjs(props.value[1])];
|
||||
} else {
|
||||
createDateRange.value = [];
|
||||
}
|
||||
} else {
|
||||
modelValue.value = props.value;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-title {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.filter {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.error-component {
|
||||
color: red;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@@ -16,7 +16,7 @@
|
||||
:before-upload="beforeUpload"
|
||||
:customRequest="customRequest"
|
||||
:file-list="fileList"
|
||||
:headers="{ 'x-access-token': useUserStore().getToken }"
|
||||
:headers="{ Authorization: 'Bearer ' + useUserStore().getToken }"
|
||||
:list-type="listType"
|
||||
@change="handleChange"
|
||||
@preview="handlePreview"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<script setup>
|
||||
import _ from 'lodash';
|
||||
import { tableColumnApi } from '/@/api/support/table-column-api';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
import { onMounted, ref, watch, reactive } from 'vue';
|
||||
import SmartTableColumnModal from './smart-table-column-modal.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { mergeColumn } from './smart-table-column-merge';
|
||||
@@ -66,17 +66,17 @@
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
// 原始表格列数据(复制一份最原始的columns集合,以供后续各个地方使用)
|
||||
let originalColumn = _.cloneDeep(props.modelValue);
|
||||
|
||||
onMounted(() => {
|
||||
buildUserTableColumns();
|
||||
// 监听全屏事件 解决按下 ESC 退出全屏 fullScreenFlag 未改变导致下次第一下点击全屏无效的问题
|
||||
addEventListener('fullscreenchange', (event) => {
|
||||
if (document.fullscreenElement === null) {
|
||||
fullScreenFlag.value = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
let originalColumn = reactive(_.cloneDeep(props.modelValue));
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(value) => {
|
||||
originalColumn = value;
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
onMounted(buildUserTableColumns);
|
||||
|
||||
//构建用户的数据列
|
||||
async function buildUserTableColumns() {
|
||||
@@ -176,7 +176,8 @@
|
||||
// 将弹窗修改的列数据,赋值给原表格 列数组
|
||||
function updateColumn(changeColumnArray) {
|
||||
//合并列
|
||||
const newColumns = mergeColumn(_.cloneDeep(originalColumn), changeColumnArray);
|
||||
let obj = mergeColumn(_.cloneDeep(originalColumn), changeColumnArray);
|
||||
const newColumns = obj.newColumns;
|
||||
emit(
|
||||
'update:modelValue',
|
||||
newColumns.filter((e) => e.showFlag)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* 表格列设置
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-08-26 23:45:51
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
*/
|
||||
/*
|
||||
* 表格列设置
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-08-26 23:45:51
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
@@ -16,6 +16,7 @@ import _ from 'lodash';
|
||||
* @param {*} userTableColumnArray
|
||||
*/
|
||||
export function mergeColumn(originalTableColumnArray, userTableColumnArray) {
|
||||
let saveFlag = false;
|
||||
if (!userTableColumnArray) {
|
||||
return originalTableColumnArray;
|
||||
}
|
||||
@@ -40,8 +41,13 @@ export function mergeColumn(originalTableColumnArray, userTableColumnArray) {
|
||||
if (userColumn) {
|
||||
fontColumn.sort = userColumn.sort;
|
||||
fontColumn.showFlag = userColumn.showFlag;
|
||||
if (userColumn.width) {
|
||||
fontColumn.width = userColumn.width;
|
||||
if (fontColumn.dragAndDropFlag) {
|
||||
saveFlag = true;
|
||||
delete fontColumn.dragAndDropFlag;
|
||||
} else {
|
||||
if (userColumn.width) {
|
||||
fontColumn.width = userColumn.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
newColumns.push(fontColumn);
|
||||
@@ -50,5 +56,8 @@ export function mergeColumn(originalTableColumnArray, userTableColumnArray) {
|
||||
|
||||
//第三步:前端列进行排序
|
||||
newColumns = _.sortBy(newColumns, (e) => e.sort);
|
||||
return newColumns;
|
||||
return {
|
||||
newColumns,
|
||||
saveFlag,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
function hide() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
const saveFlag = ref(false);
|
||||
//获取用户的列数据
|
||||
async function getUserTableColumns(tableId, columns) {
|
||||
if (!tableId) {
|
||||
@@ -128,14 +128,18 @@
|
||||
}
|
||||
|
||||
//根据前端列和后端列构建新的列数据
|
||||
tableData.value = mergeColumn(columns, userTableColumnArray);
|
||||
|
||||
let obj = mergeColumn(columns, userTableColumnArray);
|
||||
tableData.value = obj.newColumns;
|
||||
saveFlag.value = obj.saveFlag;
|
||||
//将已经显示的展示出来
|
||||
for (const item of tableData.value) {
|
||||
if (item.showFlag) {
|
||||
selectedRowKeyList.value.push(item.columnKey);
|
||||
}
|
||||
}
|
||||
if (saveFlag.value) {
|
||||
save(false);
|
||||
}
|
||||
|
||||
nextTick(() => {
|
||||
initDrag();
|
||||
@@ -253,7 +257,7 @@
|
||||
}
|
||||
|
||||
//保存
|
||||
async function save() {
|
||||
async function save(closeFlag = true) {
|
||||
submitLoading.value = true;
|
||||
try {
|
||||
let columnList = [];
|
||||
@@ -277,9 +281,11 @@
|
||||
columnList,
|
||||
});
|
||||
|
||||
message.success('保存成功');
|
||||
emit('change', columnList);
|
||||
hide();
|
||||
if (closeFlag) {
|
||||
message.success('保存成功');
|
||||
hide();
|
||||
}
|
||||
} catch (e) {
|
||||
smartSentry.captureError(e);
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div :class="className">
|
||||
<div v-if="overflow" class="ellipsis-box">
|
||||
<a-popover>
|
||||
<template #content>
|
||||
{{ text }}
|
||||
<SmartCopyIcon :value="text" />
|
||||
</template>
|
||||
<div>
|
||||
<slot>
|
||||
{{ text }}
|
||||
</slot>
|
||||
</div>
|
||||
</a-popover>
|
||||
</div>
|
||||
<div v-else>
|
||||
<slot>
|
||||
{{ text }}
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref, nextTick } from 'vue';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import SmartCopyIcon from '/@/components/smart-copy-icon/index.vue';
|
||||
|
||||
const props = defineProps({
|
||||
text: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
classKey: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const overflow = ref(false);
|
||||
const className = ref();
|
||||
onMounted(() => {
|
||||
className.value = props.classKey + uuid();
|
||||
nextTick(() => {
|
||||
let doc = document.querySelector(`.${className.value}`);
|
||||
let fontSize = window.getComputedStyle(doc).fontSize.replace('px', '');
|
||||
let clientWidth = doc.clientWidth;
|
||||
let span = document.createElement('span');
|
||||
document.body.appendChild(span);
|
||||
span.style.fontSize = `${fontSize}px`;
|
||||
span.innerText = props.text;
|
||||
let width = span.offsetWidth;
|
||||
document.body.removeChild(span);
|
||||
overflow.value = width > clientWidth;
|
||||
});
|
||||
});
|
||||
|
||||
function showModel() {
|
||||
Modal.info({
|
||||
content: props.text,
|
||||
icon: '',
|
||||
okText: '关闭',
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.ellipsis-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
div {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user