Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	web/src/settings.ts
This commit is contained in:
李强
2023-12-26 15:37:08 +08:00
15 changed files with 3102 additions and 371 deletions

View File

@@ -13,7 +13,7 @@ import {FsExtendsEditor, FsExtendsUploader} from '@fast-crud/fast-extends';
import '@fast-crud/fast-extends/dist/style.css'; import '@fast-crud/fast-extends/dist/style.css';
import {successMessage, successNotification} from '/@/utils/message'; import {successMessage, successNotification} from '/@/utils/message';
import XEUtils from "xe-utils"; import XEUtils from "xe-utils";
import {commonCrudConfig} from "/@/utils/commonCrud";
export default { export default {
async install(app: any, options: any) { async install(app: any, options: any) {
// 先安装ui // 先安装ui
@@ -110,7 +110,7 @@ export default {
successHandle(ret) { successHandle(ret) {
// 上传完成后的结果处理, 此处应返回格式为{url:xxx,key:xxx} // 上传完成后的结果处理, 此处应返回格式为{url:xxx,key:xxx}
return { return {
url: getBaseURL() + ret.data.url, url: getBaseURL(ret.data.url),
key: ret.data.id, key: ret.data.id,
...ret.data ...ret.data
}; };

View File

@@ -256,6 +256,13 @@
.el-button.is-text { .el-button.is-text {
padding: 0; padding: 0;
} }
th.el-table__cell{
-webkit-user-select: text !important;
-moz-user-select: text !important;
-ms-user-select: text !important;
user-select:text !important;
}
} }
/* scrollbar /* scrollbar

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -5,3 +5,4 @@
@import './media/media.scss'; @import './media/media.scss';
@import './waves.scss'; @import './waves.scss';
@import './dark.scss'; @import './dark.scss';
@import './fa/css/font-awesome.min.css';

View File

@@ -3,9 +3,9 @@ import { pluginsAll } from '/@/views/plugins/index';
/** /**
* @description 校验是否为租户模式。租户模式把域名替换成 域名 加端口 * @description 校验是否为租户模式。租户模式把域名替换成 域名 加端口
*/ */
export const getBaseURL = function () { export const getBaseURL = function (url:string) {
var baseURL = import.meta.env.VITE_API_URL as any; let baseURL = import.meta.env.VITE_API_URL as any;
var param = baseURL.split('/')[3] || ''; let param = baseURL.split('/')[3] || '';
// @ts-ignore // @ts-ignore
if (pluginsAll && pluginsAll.indexOf('dvadmin3-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) { if (pluginsAll && pluginsAll.indexOf('dvadmin3-tenants-web') !== -1 && (!param || baseURL.startsWith('/'))) {
// 1.把127.0.0.1 替换成和前端一样域名 // 1.把127.0.0.1 替换成和前端一样域名
@@ -26,6 +26,16 @@ export const getBaseURL = function () {
baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL; baseURL = location.protocol + '//' + location.hostname + (location.port ? ':' : '') + location.port + baseURL;
} }
} }
if(url){
const regex = /^(http|https):\/\//;
if(regex.test(url)){
return url
}else{
if(url.startsWith('/')){
return baseURL + url;
}
}
}
if (!baseURL.endsWith('/')) { if (!baseURL.endsWith('/')) {
baseURL += '/'; baseURL += '/';
} }

View File

@@ -50,6 +50,17 @@ const getAwesomeIconfont = () => {
const styles: any = document.styleSheets; const styles: any = document.styleSheets;
let sheetsList = []; let sheetsList = [];
let sheetsIconList = []; let sheetsIconList = [];
// 判断fontFamily是否是本地加载
for (let i = 0; i < styles.length; i++) {
const rules = styles[i].cssRules || styles[i].rules;
if (rules) {
for (let j = 0; j < rules.length; j++) {
if (rules[j].style && rules[j].style.fontFamily === 'FontAwesome') {
sheetsList.push(styles[i])
}
}
}
}
for (let i = 0; i < styles.length; i++) { for (let i = 0; i < styles.length; i++) {
if (styles[i].href && styles[i].href.indexOf('netdna.bootstrapcdn.com') > -1) { if (styles[i].href && styles[i].href.indexOf('netdna.bootstrapcdn.com') > -1) {
sheetsList.push(styles[i]); sheetsList.push(styles[i]);

View File

@@ -2,7 +2,7 @@
const cssCdnUrlList: Array<string> = [ const cssCdnUrlList: Array<string> = [
'//at.alicdn.com/t/font_2298093_y6u00apwst.css', '//at.alicdn.com/t/font_2298093_y6u00apwst.css',
'//at.alicdn.com/t/c/font_3882322_9ah7y8m9175.css', //dvadmin3项目用icon '//at.alicdn.com/t/c/font_3882322_9ah7y8m9175.css', //dvadmin3项目用icon
'//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' //'//netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'
]; ];
// 第三方 js url // 第三方 js url
const jsCdnUrlList: Array<string> = []; const jsCdnUrlList: Array<string> = [];

View File

@@ -48,9 +48,9 @@
| |
<a :href="getSystemConfig['login.help_url']?getSystemConfig['login.help_url']:'https://django-vue-admin.com'" target="_blank">帮助</a> <a :href="getSystemConfig['login.help_url']?getSystemConfig['login.help_url']:'https://django-vue-admin.com'" target="_blank">帮助</a>
| |
<a :href="getSystemConfig['login.privacy_url']?getSystemConfig['login.privacy_url']:'#'">隐私</a> <a :href="getSystemConfig['login.privacy_url']?getBaseURL(getSystemConfig['login.privacy_url']):'#'">隐私</a>
| |
<a :href="getSystemConfig['login.clause_url']?getSystemConfig['login.clause_url']:'#'">条款</a> <a :href="getSystemConfig['login.clause_url']?getBaseURL(getSystemConfig['login.clause_url']):'#'">条款</a>
</p> </p>
</div> </div>
</div> </div>
@@ -65,6 +65,7 @@ import logoMini from '/@/assets/logo-mini.svg';
import loginMain from '/@/assets/login-main.svg'; import loginMain from '/@/assets/login-main.svg';
import loginBg from '/@/assets/login-bg.svg'; import loginBg from '/@/assets/login-bg.svg';
import {SystemConfigStore} from '/@/stores/systemConfig' import {SystemConfigStore} from '/@/stores/systemConfig'
import {getBaseURL} from "/@/utils/baseUrl";
// 引入组件 // 引入组件
const Account = defineAsyncComponent(() => import('/@/views/system/login/component/account.vue')); const Account = defineAsyncComponent(() => import('/@/views/system/login/component/account.vue'));
const Mobile = defineAsyncComponent(() => import('/@/views/system/login/component/mobile.vue')); const Mobile = defineAsyncComponent(() => import('/@/views/system/login/component/mobile.vue'));

View File

@@ -1,370 +1,396 @@
import * as api from './api'; import * as api from './api';
import { dict, UserPageQuery, AddReq, DelReq, EditReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud'; import {
import { request } from '/@/utils/service'; dict,
import { dictionary } from '/@/utils/dictionary'; UserPageQuery,
import { successMessage } from '/@/utils/message'; AddReq,
import { auth } from '/@/utils/authFunction'; DelReq,
EditReq,
compute,
CreateCrudOptionsProps,
CreateCrudOptionsRet
} from '@fast-crud/fast-crud';
import {request} from '/@/utils/service';
import {dictionary} from '/@/utils/dictionary';
import {successMessage} from '/@/utils/message';
import {auth} from '/@/utils/authFunction';
import {SystemConfigStore} from "/@/stores/systemConfig";
import {storeToRefs} from "pinia";
import {computed} from "vue";
import { Md5 } from 'ts-md5';
export const createCrudOptions = function ({crudExpose}: CreateCrudOptionsProps): CreateCrudOptionsRet {
const pageRequest = async (query: UserPageQuery) => {
return await api.GetList(query);
};
const editRequest = async ({form, row}: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({row}: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({form}: AddReq) => {
return await api.AddObj(form);
};
export const createCrudOptions = function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet { const exportRequest = async (query: UserPageQuery) => {
const pageRequest = async (query: UserPageQuery) => { return await api.exportData(query)
return await api.GetList(query); }
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
const exportRequest = async (query: UserPageQuery) => { const systemConfigStore = SystemConfigStore()
return await api.exportData(query) const {systemConfig} = storeToRefs(systemConfigStore)
} const getSystemConfig = computed(() => {
console.log(systemConfig.value)
return systemConfig.value
})
return { return {
crudOptions: { crudOptions: {
table: { table: {
remove: { remove: {
confirmMessage: '是否删除该用户?', confirmMessage: '是否删除该用户?',
}, },
}, },
request: { request: {
pageRequest, pageRequest,
addRequest, addRequest,
editRequest, editRequest,
delRequest, delRequest,
}, },
actionbar: { form: {
buttons: { initialForm: {
add: { password: computed(() => {
show: auth('user:Create') return systemConfig.value['base.default_password']
}, }),
export:{ }
text:"导出",//按钮文字 },
title:"导出",//鼠标停留显示的信息 actionbar: {
click(){ buttons: {
return exportRequest(crudExpose!.getSearchFormData()) add: {
} show: auth('user:Create')
} },
} export: {
}, text: "导出",//按钮文字
rowHandle: { title: "导出",//鼠标停留显示的信息
//固定右侧 click() {
fixed: 'right', return exportRequest(crudExpose!.getSearchFormData())
width: 200,
buttons: {
view: {
show: false,
},
edit: {
iconRight: 'Edit',
type: 'text',
show: auth('user:Update'),
},
remove: {
iconRight: 'Delete',
type: 'text',
show: auth('user:Delete'),
},
custom: {
text: '重设密码',
type: 'text',
show: auth('user:ResetPassword'),
tooltip: {
placement: 'top',
content: '重设密码',
},
//@ts-ignore
click: (ctx: any) => {
const { row } = ctx;
},
},
},
},
columns: {
_index: {
title: '序号',
form: { show: false },
column: {
type: 'index',
align: 'center',
width: '70px',
columnSetDisabled: true, //禁止在列设置中选择
},
},
username: {
title: '账号',
search: {
show: true,
},
type: 'input',
column: {
minWidth: 100, //最小列宽
},
form: {
rules: [
// 表单校验规则
{
required: true,
message: '账号必填项',
},
],
component: {
placeholder: '请输入账号',
},
},
},
password: {
title: '密码',
type: 'input',
column: {
show: false,
},
editForm: {
show: false,
},
form: {
rules: [
// 表单校验规则
{
required: true,
message: '密码必填项',
},
],
component: {
span: 12,
showPassword: true,
placeholder: '请输入密码',
},
// value: vm.systemConfig('base.default_password'),
},
/* valueResolve(row, key) {
if (row.password) {
row.password = vm.$md5(row.password)
} }
} */ }
}, }
name: { },
title: '姓名', rowHandle: {
search: { //固定右侧
show: true, fixed: 'right',
}, width: 200,
type: 'input', buttons: {
column: { view: {
minWidth: 100, //最小列宽 show: false,
}, },
form: { edit: {
rules: [ iconRight: 'Edit',
// 表单校验规则 type: 'text',
{ show: auth('user:Update'),
required: true, },
message: '姓名必填项', remove: {
}, iconRight: 'Delete',
], type: 'text',
component: { show: auth('user:Delete'),
span: 12, },
placeholder: '请输入姓名', custom: {
}, text: '重设密码',
}, type: 'text',
}, show: auth('user:ResetPassword'),
dept: { tooltip: {
title: '部门', placement: 'top',
search: { content: '重设密码',
disabled: true, },
}, //@ts-ignore
type: 'dict-tree', click: (ctx: any) => {
dict: dict({ const {row} = ctx;
isTree: true, },
url: '/api/system/dept/all_dept/', },
value: 'id', },
label: 'name', },
getData: async ({ url }: { url: string }) => { columns: {
return request({ _index: {
url: url, title: '序号',
}).then((ret: any) => { form: {show: false},
return ret.data; column: {
}); type: 'index',
}, align: 'center',
}), width: '70px',
column: { columnSetDisabled: true, //禁止在列设置中选择
minWidth: 150, //最小列宽 },
}, },
form: { username: {
rules: [ title: '账号',
// 表单校验规则 search: {
{ show: true,
required: true, },
message: '必填项', type: 'input',
}, column: {
], minWidth: 100, //最小列宽
component: { },
filterable: true, form: {
placeholder: '请选择', rules: [
props: { // 表单校验规则
props: { {
value: 'id', required: true,
label: 'name', message: '账号必填项',
}, },
}, ],
}, component: {
}, placeholder: '请输入账号',
}, },
role: { },
title: '角色', },
search: { password: {
disabled: true, title: '密码',
}, type: 'password',
type: 'dict-select', column: {
dict: dict({ show: false,
url: '/api/system/role/', },
value: 'id', editForm: {
label: 'name', show: false,
isTree: true, },
getData: async ({ url }: { url: string }) => { form: {
return request({ rules: [
url: url, // 表单校验规则
params: { {
page: 1, required: true,
limit: 10, message: '密码必填项',
}, },
}).then((ret: any) => { ],
return ret.data; component: {
});
}, span: 12,
}), showPassword: true,
column: { placeholder: '请输入密码',
minWidth: 100, //最小列宽 },
}, },
form: { valueResolve({form}) {
rules: [ if (form.password) {
// 表单校验规则 form.password = Md5.hashStr(form.password)
{ }
required: true, }
message: '必填项', },
}, name: {
], title: '姓名',
component: { search: {
multiple: true, show: true,
filterable: true, },
placeholder: '请选择角色', type: 'input',
}, column: {
}, minWidth: 100, //最小列宽
}, },
mobile: { form: {
title: '手机号码', rules: [
search: { // 表单校验规则
show: true, {
}, required: true,
type: 'input', message: '姓名必填项',
column: { },
minWidth: 120, //最小列宽 ],
}, component: {
form: { span: 12,
rules: [ placeholder: '请输入姓名',
{ },
max: 20, },
message: '请输入正确的手机号码', },
trigger: 'blur', dept: {
}, title: '部门',
{ search: {
pattern: /^1[3-9]\d{9}$/, disabled: true,
message: '请输入正确的手机号码', },
}, type: 'dict-tree',
], dict: dict({
component: { isTree: true,
placeholder: '请输入手机号码', url: '/api/system/dept/all_dept/',
}, value: 'id',
}, label: 'name',
}, getData: async ({url}: { url: string }) => {
email: { return request({
title: '邮箱', url: url,
column: { }).then((ret: any) => {
width: 260, return ret.data;
}, });
form: { },
rules: [ }),
{ column: {
type: 'email', minWidth: 150, //最小列宽
message: '请输入正确的邮箱地址', },
trigger: ['blur', 'change'], form: {
}, rules: [
], // 表单校验规则
component: { {
placeholder: '请输入邮箱', required: true,
}, message: '必填项',
}, },
}, ],
gender: { component: {
title: '性别', filterable: true,
type: 'dict-select', placeholder: '请选择',
dict: dict({ props: {
data: dictionary('gender'), props: {
}), value: 'id',
form: { label: 'name',
value: 1, },
component: { },
span: 12, },
}, },
}, },
component: { props: { color: 'auto' } }, // 自动染色 role: {
}, title: '角色',
user_type: { search: {
title: '用户类型', disabled: true,
search: { },
show: true, type: 'dict-select',
}, dict: dict({
type: 'dict-select', url: '/api/system/role/',
dict: dict({ value: 'id',
data: dictionary('user_type'), label: 'name',
}), isTree: true,
column: { getData: async ({url}: { url: string }) => {
minWidth: 100, //最小列宽 return request({
}, url: url,
form: { params: {
show: false, page: 1,
value: 0, limit: 10,
component: { },
span: 12, }).then((ret: any) => {
}, return ret.data;
}, });
}, },
is_active: { }),
title: '锁定', column: {
search: { minWidth: 100, //最小列宽
show: true, },
}, form: {
type: 'dict-radio', rules: [
column: { // 表单校验规则
component: { {
name: 'fs-dict-switch', required: true,
activeText: '', message: '必填项',
inactiveText: '', },
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6', ],
onChange: compute((context) => { component: {
return () => { multiple: true,
api.UpdateObj(context.row).then((res: APIResponseData) => { filterable: true,
successMessage(res.msg as string); placeholder: '请选择角色',
}); },
}; },
}), },
}, mobile: {
}, title: '手机号码',
dict: dict({ search: {
data: dictionary('button_status_bool'), show: true,
}), },
}, type: 'input',
avatar: { column: {
title: '头像', minWidth: 120, //最小列宽
type: 'avatar-cropper', },
form: { form: {
show: false, rules: [
}, {
}, max: 20,
}, message: '请输入正确的手机号码',
}, trigger: 'blur',
}; },
{
pattern: /^1[3-9]\d{9}$/,
message: '请输入正确的手机号码',
},
],
component: {
placeholder: '请输入手机号码',
},
},
},
email: {
title: '邮箱',
column: {
width: 260,
},
form: {
rules: [
{
type: 'email',
message: '请输入正确的邮箱地址',
trigger: ['blur', 'change'],
},
],
component: {
placeholder: '请输入邮箱',
},
},
},
gender: {
title: '性别',
type: 'dict-select',
dict: dict({
data: dictionary('gender'),
}),
form: {
value: 1,
component: {
span: 12,
},
},
component: {props: {color: 'auto'}}, // 自动染色
},
user_type: {
title: '用户类型',
search: {
show: true,
},
type: 'dict-select',
dict: dict({
data: dictionary('user_type'),
}),
column: {
minWidth: 100, //最小列宽
},
form: {
show: false,
value: 0,
component: {
span: 12,
},
},
},
is_active: {
title: '锁定',
search: {
show: true,
},
type: 'dict-radio',
column: {
component: {
name: 'fs-dict-switch',
activeText: '',
inactiveText: '',
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
onChange: compute((context) => {
return () => {
api.UpdateObj(context.row).then((res: APIResponseData) => {
successMessage(res.msg as string);
});
};
}),
},
},
dict: dict({
data: dictionary('button_status_bool'),
}),
},
avatar: {
title: '头像',
type: 'avatar-cropper',
form: {
show: false,
},
},
},
},
};
}; };