首页的统计图和票据的状态分标签展示

This commit is contained in:
2024-04-17 11:23:49 +08:00
parent b840ca4974
commit f070b02da9
46 changed files with 7371 additions and 6675 deletions

View File

@ -13,8 +13,8 @@ export default mergeConfig(
}, },
proxy: { proxy: {
'/api': { '/api': {
target: 'http://59.110.238.182:8081', target: 'http://106.53.179.133:8081',
// target: 'http://192.168.3.158:8081', // target: 'http://192.168.243.246:8081',
// target: 'http://localhost:5173', // target: 'http://localhost:5173',
changeOrigin: true, changeOrigin: true,
}, },

View File

@ -3,10 +3,9 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="shortcut icon" type="image/x-icon" <link rel="icon" href="./src/image/票据服务.png" type="image/x-icon">
href="https://unpkg.byted-static.com/latest/byted/arco-config/assets/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>中山学院学习平台</title> <title>票据管理系统</title>
</head> </head>
<body> <body>

View File

@ -13,10 +13,7 @@
"type:check": "vue-tsc --noEmit --skipLibCheck", "type:check": "vue-tsc --noEmit --skipLibCheck",
"lint-staged": "npx lint-staged", "lint-staged": "npx lint-staged",
"prepare": "husky install", "prepare": "husky install",
"start": "npm run dev" "start": "npm run dev"
}, },
"lint-staged": { "lint-staged": {
"*.{js,ts,jsx,tsx}": [ "*.{js,ts,jsx,tsx}": [
@ -40,6 +37,8 @@
"axios": "^0.24.0", "axios": "^0.24.0",
"dayjs": "^1.11.5", "dayjs": "^1.11.5",
"echarts": "^5.4.0", "echarts": "^5.4.0",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
@ -50,12 +49,14 @@
"vue": "^3.2.40", "vue": "^3.2.40",
"vue-echarts": "^6.2.3", "vue-echarts": "^6.2.3",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-router": "^4.0.14" "vue-router": "^4.0.14",
"xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@arco-plugins/vite-vue": "^1.4.5", "@arco-plugins/vite-vue": "^1.4.5",
"@commitlint/cli": "^17.1.2", "@commitlint/cli": "^17.1.2",
"@commitlint/config-conventional": "^17.1.0", "@commitlint/config-conventional": "^17.1.0",
"@types/file-saver": "^2.0.7",
"@types/lodash": "^4.14.186", "@types/lodash": "^4.14.186",
"@types/mockjs": "^1.0.7", "@types/mockjs": "^1.0.7",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",

View File

@ -75,10 +75,12 @@ axios.interceptors.response.use(
const { response } = error; const { response } = error;
console.log('error', error); console.log('error', error);
if (response.status === '401') { if (response.status === 401) {
router.push({ router.push({
name: 'login', name: 'login',
}); });
} }
else { else {
Message.error({ Message.error({

View File

@ -18,8 +18,13 @@ export interface RoleListRecord extends RoleRecord {
} }
// 查询所有的角色列表、 // 查询所有的角色列表、
export function queryRoleList() { export function queryRoleList(data: any) {
return axios.get('/api/rest/role'); // return axios.get('/api/rest/role',data);
return axios({
url: '/api/rest/role', // 路径
method: 'get',
params: data, // 参数
})
} }
// 切换启用状态 // 切换启用状态

View File

@ -7,7 +7,7 @@ export interface TicketCreateRecord {
type: string; type: string;
contactEmail: string; contactEmail: string;
companyName: string; companyName: string;
attachId: string; attachId: [];
auditorId: string; auditorId: string;
submit: boolean; submit: boolean;
userId: undefined; userId: undefined;
@ -15,6 +15,9 @@ export interface TicketCreateRecord {
} }
export interface TicketRecord extends TicketCreateRecord { export interface TicketRecord extends TicketCreateRecord {
auditor: any;
createTime(createTime: any): unknown;
deptName: any;
value: any; value: any;
id: undefined; id: undefined;
status: string status: string
@ -83,3 +86,8 @@ export function home(data: any){
params: data, // 参数 params: data, // 参数
}); });
} }
// 首页统计图
export function chart(){
return axios.get('/api/rest/bill/trend')
}

View File

@ -29,6 +29,8 @@ export interface PasswordReSetModel {
// 添加用户数据 // 添加用户数据
export interface CreateRecord { export interface CreateRecord {
value: any;
code: any;
username: string; username: string;
nickName: string; nickName: string;
@ -107,10 +109,15 @@ export function resetPassword(data: PasswordReSetModel) {
} }
// 注册用户 // 注册用户
export function create(data: CreateRecord) { export function register(data: CreateRecord) {
return axios.post('/api/rest/user/register', data); return axios.post('/api/rest/user/register', data);
} }
// 新建用户
export function create(data: CreateRecord) {
return axios.post('/api/rest/user', data);
}
// 模糊查询用户列表 // 模糊查询用户列表
export function queryUserList(params: any) { export function queryUserList(params: any) {
return axios({ return axios({
@ -158,6 +165,11 @@ export function deptAudit(id: string,roleId:string){
}); });
} }
// 获取验证码
export function code(data: string){
return axios.get(`/api/rest/user/send-email?email=${data}`);
}
export function switchRole(roleId: number) { export function switchRole(roleId: number) {
return axios.patch<UserState>(`/api/user/self/switch-role/${roleId}`); return axios.patch<UserState>(`/api/user/self/switch-role/${roleId}`);
} }

View File

@ -1,16 +1,16 @@
<template> <template>
<a-layout-footer class="footer">Arco Pro</a-layout-footer> <a-layout-footer class="footer"></a-layout-footer>
</template> </template>
<script lang="ts" setup></script> <script lang="ts" setup></script>
<style lang="less" scoped> <style lang="less" scoped>
.footer { .footer {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 40px; height: 40px;
color: var(--color-text-2); color: var(--color-text-2);
text-align: center; text-align: center;
} }
</style> </style>

View File

@ -2,10 +2,7 @@
<div class="navbar"> <div class="navbar">
<div class="left-side"> <div class="left-side">
<a-space> <a-space>
<img <img style="height: 24px" alt="logo" src="../../image/票据服务.png" />
alt="logo"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image"
/>
<a-typography-title <a-typography-title
:style="{ margin: 0, fontSize: '18px' }" :style="{ margin: 0, fontSize: '18px' }"
:heading="5" :heading="5"
@ -112,6 +109,9 @@
<li> <li>
<a-dropdown trigger="click"> <a-dropdown trigger="click">
<a-button style="margin-right: 20px" status="normal">{{
userStore.nickName
}}</a-button>
<a-avatar <a-avatar
:size="32" :size="32"
:style="{ marginRight: '8px', cursor: 'pointer' }" :style="{ marginRight: '8px', cursor: 'pointer' }"

View File

@ -8,7 +8,7 @@
"menuCollapse": false, "menuCollapse": false,
"footer": true, "footer": true,
"themeColor": "#165DFF", "themeColor": "#165DFF",
"menuWidth": 220, "menuWidth": 200,
"globalSettings": false, "globalSettings": false,
"device": "desktop", "device": "desktop",
"tabBar": false, "tabBar": false,

BIN
src/image/票据服务.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -9,7 +9,7 @@ const SYSTEM: AppRouteRecordRaw = {
locale: 'menu.system', locale: 'menu.system',
icon: 'icon-computer', icon: 'icon-computer',
requiresAuth: true, requiresAuth: true,
order: 1, order: 2,
}, },
children: [ children: [
// { // {

View File

@ -9,7 +9,7 @@ const TICKET: AppRouteRecordRaw = {
locale: 'menu.ticket', locale: 'menu.ticket',
icon: 'icon-folder-add', icon: 'icon-folder-add',
requiresAuth: true, requiresAuth: true,
order: 4, order: 1,
}, },
children: [ children: [
{ {

View File

@ -9,7 +9,7 @@ const USER: AppRouteRecordRaw = {
locale: 'menu.user', locale: 'menu.user',
icon: 'icon-user', icon: 'icon-user',
requiresAuth: true, requiresAuth: true,
order: 7, order: 3,
}, },
children: [ children: [
// { // {

View File

@ -27,8 +27,8 @@ const useRoleStore = defineStore('role', {
}, },
actions: { actions: {
// 获取所有的角色列表 // 获取所有的角色列表
async getRoleList() { async getRoleList(params: any) {
return queryRoleList(); return queryRoleList(params);
}, },
async enabledRole(id: string) { async enabledRole(id: string) {

View File

@ -11,7 +11,8 @@ import {
audit, audit,
uploadFile, uploadFile,
attachment, attachment,
home home,
chart,
} from '@/api/ticket'; } from '@/api/ticket';
import { ticketStore } from './type'; import { ticketStore } from './type';
@ -61,7 +62,7 @@ const useTicketStore = defineStore('ticket', {
return update(data); return update(data);
}, },
async uploadFileTicket(file: any) { async uploadFile(file: any) {
return uploadFile(file); return uploadFile(file);
}, },
@ -75,7 +76,11 @@ const useTicketStore = defineStore('ticket', {
async getHome(data: any){ async getHome(data: any){
return home(data); return home(data);
} },
async getChart(){
return chart();
},
}, },
}); });

View File

@ -10,10 +10,12 @@ import {
enabled, enabled,
remove, remove,
create, create,
register,
UserRecord, UserRecord,
update, update,
userDetail, userDetail,
deptAudit deptAudit,
code
} from '@/api/user'; } from '@/api/user';
import { setToken, clearToken } from '@/utils/auth'; import { setToken, clearToken } from '@/utils/auth';
import { removeRouteListener } from '@/utils/route-listener'; import { removeRouteListener } from '@/utils/route-listener';
@ -92,11 +94,16 @@ const useUserStore = defineStore('user', {
return remove(id); return remove(id);
}, },
// Register user // Create user
async createUser(data: UserRecord) { async createUser(data: UserRecord) {
return create(data); return create(data);
}, },
// register user
async registerUser(data: UserRecord) {
return register(data);
},
// Update user // Update user
async updateUser(data: UserRecord) { async updateUser(data: UserRecord) {
return update(data); return update(data);
@ -110,6 +117,10 @@ const useUserStore = defineStore('user', {
return deptAudit(deptId,roleId) return deptAudit(deptId,roleId)
}, },
async getCode(params: string){
return code(params)
},
logoutCallBack() { logoutCallBack() {
const appStore = useAppStore(); const appStore = useAppStore();
this.resetInfo(); this.resetInfo();

View File

@ -26,6 +26,8 @@ export interface PostData {
export interface Pagination { export interface Pagination {
page: number; page: number;
size: number; size: number;
current: number;
total: null | number;
} }
export type TimeRanger = [string, string]; export type TimeRanger = [string, string];

69
src/utils/excel.ts Normal file
View File

@ -0,0 +1,69 @@
// 新建 @/utils/excel.ts
import saveAs from 'file-saver'; // https://www.npmjs.com/package/file-saver
import ExcelJS from 'exceljs'; // https://github.com/exceljs/exceljs/blob/master/README_zh.md
import dayjs from 'dayjs'; // https://dayjs.fenxianglu.cn/
import * as XLSX from 'xlsx'; // https://www.npmjs.com/package/xlsx
import { Message } from '@arco-design/web-vue'; // https://arco.design/vue/component/message
import { FileItem } from '@arco-design/web-vue/es/upload/interfaces'; // arco类型
export interface DownloadExcelPrams {
columns: { title: string, key: string }[];
rows: object[];
name: string
}
// 导出下载文件
export function downloadExcel({ columns, rows, name = '未命名文件' }: DownloadExcelPrams) {
const workbook = new ExcelJS.Workbook();
workbook.creator = 'Start-front';
workbook.lastModifiedBy = 'Start-front';
workbook.created = new Date(1985, 8, 30);
workbook.modified = new Date();
workbook.lastPrinted = new Date(2016, 9, 27);
// 将工作簿添加一个sheet页sheet1
const sheet1 = workbook.addWorksheet(name);
// 表头数据添加
sheet1.columns = columns.map(item => ({
header: item.title,
key: item.key,
width: 20
}));
// 表格内容添加
rows.map(item => sheet1.addRow(item));
workbook.xlsx.writeBuffer().then(buffer => {
saveAs(
new Blob([buffer], { type: 'application/octet-stream' }),
`${name}.xlsx`
);
});
};
// 读取文件为json格式
export function readExcle(fileItem:FileItem) {
console.log('读取文件...',fileItem);
return new Promise((resove,reject)=>{
try {
let workbook:XLSX.Sheet;
const reader = new FileReader();
reader.readAsBinaryString(fileItem.file as File); // 发起异步请求
reader.onload = function(ev){
const data = ev.target?.result;
workbook = XLSX.read(data, {type: 'binary'});
const sheetNames = workbook.SheetNames; // 工作表名称集合
sheetNames.forEach((name:string) => {
const worksheet = workbook.Sheets[name]; // 只能通过工作表名称来获取指定工作表
const jsonres = XLSX.utils.sheet_to_json(worksheet);
resove(jsonres)
});
} // onload
} catch (error) {
Message.error('读取失败,请选择正确文件');
reject(error);
}
})
}

View File

@ -1,114 +1,122 @@
<template> <template>
<a-grid :cols="24" :row-gap="16" class="panel"> <div style="width: 100%">
<a-grid-item <a-grid :cols="24" :row-gap="16" class="panel">
class="panel-col" <a-grid-item
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }" class="panel-col"
> :span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
<a-space> >
<a-avatar :size="54" class="col-avatar"> <a-space>
<img <a-avatar :size="54" class="col-avatar">
alt="avatar" <img
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/288b89194e657603ff40db39e8072640.svg~tplv-49unhts6dw-image.image" alt="avatar"
/> src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/288b89194e657603ff40db39e8072640.svg~tplv-49unhts6dw-image.image"
</a-avatar> />
<a-statistic </a-avatar>
:title="$t('workplace.pass')" <a-statistic
:value="formData.pass || 0" :title="$t('workplace.pass')"
:value-from="0" :value="formData.pass || 0"
animation :value-from="0"
show-group-separator animation
> show-group-separator
<template #suffix> >
<span class="unit">{{ $t('workplace.pecs') }}</span> <template #suffix>
</template> <span class="unit">{{ $t('workplace.pecs') }}</span>
</a-statistic> </template>
</a-space> </a-statistic>
</a-grid-item> </a-space>
<a-grid-item </a-grid-item>
class="panel-col" <a-grid-item
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }" class="panel-col"
> :span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
<a-space> >
<a-avatar :size="54" class="col-avatar"> <a-space>
<img <a-avatar :size="54" class="col-avatar">
alt="avatar" <img
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/fdc66b07224cdf18843c6076c2587eb5.svg~tplv-49unhts6dw-image.image" alt="avatar"
/> src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/fdc66b07224cdf18843c6076c2587eb5.svg~tplv-49unhts6dw-image.image"
</a-avatar> />
<a-statistic </a-avatar>
:title="$t('workplace.notPass')" <a-statistic
:value="formData.notPass || 0" :title="$t('workplace.notPass')"
:value-from="0" :value="formData.notPass || 0"
animation :value-from="0"
show-group-separator animation
> show-group-separator
<template #suffix> >
<span class="unit">{{ $t('workplace.pecs') }}</span> <template #suffix>
</template> <span class="unit">{{ $t('workplace.pecs') }}</span>
</a-statistic> </template>
</a-space> </a-statistic>
</a-grid-item> </a-space>
<a-grid-item </a-grid-item>
class="panel-col" <a-grid-item
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }" class="panel-col"
> :span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
<a-space> >
<a-avatar :size="54" class="col-avatar"> <a-space>
<img <a-avatar :size="54" class="col-avatar">
alt="avatar" <img
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/77d74c9a245adeae1ec7fb5d4539738d.svg~tplv-49unhts6dw-image.image" alt="avatar"
/> src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/77d74c9a245adeae1ec7fb5d4539738d.svg~tplv-49unhts6dw-image.image"
</a-avatar> />
<a-statistic </a-avatar>
:title="$t('workplace.notAudit')" <a-statistic
:value="formData.notAudit || 0" :title="$t('workplace.notAudit')"
:value-from="0" :value="formData.notAudit || 0"
animation :value-from="0"
show-group-separator animation
> show-group-separator
<template #suffix> >
<span class="unit">{{ $t('workplace.pecs') }}</span> <template #suffix>
</template> <span class="unit">{{ $t('workplace.pecs') }}</span>
</a-statistic> </template>
</a-space> </a-statistic>
</a-grid-item> </a-space>
<a-grid-item </a-grid-item>
v-if="userStore.permissions !== 'auditor'" <a-grid-item
class="panel-col" v-if="userStore.permissions !== 'auditor'"
:span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }" class="panel-col"
style="border-right: none" :span="{ xs: 12, sm: 12, md: 12, lg: 12, xl: 12, xxl: 6 }"
> style="border-right: none"
<a-space> >
<a-avatar :size="54" class="col-avatar"> <a-space>
<img <a-avatar :size="54" class="col-avatar">
alt="avatar" <img
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/c8b36e26d2b9bb5dbf9b74dd6d7345af.svg~tplv-49unhts6dw-image.image" alt="avatar"
/> src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/c8b36e26d2b9bb5dbf9b74dd6d7345af.svg~tplv-49unhts6dw-image.image"
</a-avatar> />
<a-statistic </a-avatar>
v-model="formData.notFiled" <a-statistic
:title="$t('workplace.notFiled')" v-model="formData.notFiled"
:value="formData.notFiled || 0" :title="$t('workplace.notFiled')"
:value-from="0" :value="formData.notFiled || 0"
animation :value-from="0"
show-group-separator animation
> show-group-separator
<template #suffix> >
<span class="unit">{{ $t('workplace.pecs') }}</span> <template #suffix>
</template> <span class="unit">{{ $t('workplace.pecs') }}</span>
</a-statistic> </template>
</a-space> </a-statistic>
</a-grid-item> </a-space>
<a-grid-item :span="24"> </a-grid-item>
<a-divider class="panel-border" /> <a-grid-item :span="24">
</a-grid-item> <a-divider class="panel-border" />
</a-grid> </a-grid-item>
</a-grid>
<a-card :bordered="false" v-if="userStore.permissions === 'admin'">
<a-space id="TicketEcharts" style="width: 98%; height: 400px"></a-space>
</a-card>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import { useTicketStore, useUserStore } from '@/store'; import { useTicketStore, useUserStore } from '@/store';
import { ref } from 'vue'; import { onMounted, ref } from 'vue';
import * as echarts from 'echarts';
const { t } = useI18n();
const ticketStore = useTicketStore(); const ticketStore = useTicketStore();
const userStore = useUserStore(); const userStore = useUserStore();
const formData = ref({ const formData = ref({
@ -118,6 +126,72 @@ const formData = ref({
pass: undefined, pass: undefined,
}); });
const getEchartData = () => {
const res = ticketStore.getChart();
console.log('chart', res);
};
onMounted(async () => {
const res = await ticketStore.getChart();
console.log('chart', res.data);
type EChartsOption = echarts.EChartsOption;
const chartDom = document.getElementById('TicketEcharts');
const myChart = echarts.init(chartDom);
const option = {
title: {
text: t('workplace.chart'),
},
tooltip: {
trigger: 'axis',
},
legend: {
data: [
t('workplace.pass'),
t('workplace.notPass'),
t('workplace.notAudit'),
],
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
toolbox: {
feature: {
saveAsImage: {},
},
},
xAxis: {
type: 'category',
boundaryGap: false,
data: res.data.dates,
},
yAxis: {
type: 'value',
},
series: [
{
name: t('workplace.pass'),
type: 'line',
data: res.data.status2Counts,
},
{
name: t('workplace.notPass'),
type: 'line',
data: res.data.status3Counts,
},
{
name: t('workplace.notAudit'),
type: 'line',
data: res.data.status1Counts,
},
],
};
myChart.setOption(option);
});
const getHomeData = async (params: { const getHomeData = async (params: {
auditorId: number | undefined; auditorId: number | undefined;
userId: number | string | undefined; userId: number | string | undefined;

View File

@ -40,4 +40,6 @@ export default {
'workplace.notPass': 'notPass', 'workplace.notPass': 'notPass',
'workplace.notAudit': 'notAudit', 'workplace.notAudit': 'notAudit',
'workplace.notFiled': 'notFiled', 'workplace.notFiled': 'notFiled',
'workplace.total': 'total',
'workplace.chart':' Ticket Chart'
}; };

View File

@ -38,4 +38,6 @@ export default {
'workplace.notPass': '审核不通过', 'workplace.notPass': '审核不通过',
'workplace.notAudit': '待审核', 'workplace.notAudit': '待审核',
'workplace.notFiled': '待提交', 'workplace.notFiled': '待提交',
'workplace.total': '票据总数',
'workplace.chart':'票据分析图'
}; };

View File

@ -94,6 +94,40 @@
:placeholder="$t('user.info.username.placeholder')" :placeholder="$t('user.info.username.placeholder')"
/> />
</a-form-item> </a-form-item>
<a-form-item
field="email"
:label="$t('user.info.email')"
:rules="[
{
required: true,
type: 'email',
message: t('user.info.email.required'),
},
]"
:validate-trigger="['change', 'input']"
>
<a-input
v-model="formData.email"
:placeholder="$t('user.info.emailCode.placeholder')"
/>
</a-form-item>
<a-form-item
field="phone"
:label="$t('user.info.phone')"
:rules="[
{ required: true, message: t('user.info.phone.required') },
{ match: /^1[3-9]\d{9}$/, message: t('user.info.phone.format') },
]"
:validate-trigger="['change', 'input']"
>
<a-input
v-model="formData.phone"
:placeholder="$t('user.info.phone.placeholder')"
/>
</a-form-item>
<a-form-item <a-form-item
field="password" field="password"
:label="$t('user.info.password')" :label="$t('user.info.password')"
@ -111,43 +145,37 @@
:placeholder="$t('user.info.nickName.placeholder')" :placeholder="$t('user.info.nickName.placeholder')"
/> />
</a-form-item> </a-form-item>
<a-form-item
field="phone"
:label="$t('user.info.phone')"
:rules="[
{ required: true, message: t('user.info.phone.required') },
{ match: /^1[3-9]\d{9}$/, message: t('user.info.phone.format') },
]"
:validate-trigger="['change', 'input']"
>
<a-input
v-model="formData.phone"
:placeholder="$t('user.info.phone.placeholder')"
/>
</a-form-item>
<a-form-item
field="email"
:label="$t('user.info.email')"
:rules="[
{
required: true,
type: 'email',
message: t('user.info.email.required'),
},
]"
:validate-trigger="['change', 'input']"
>
<a-input
v-model="formData.email"
:placeholder="$t('user.info.email.placeholder')"
/>
</a-form-item>
<a-form-item field="address" :label="$t('user.info.address')"> <a-form-item field="address" :label="$t('user.info.address')">
<a-input <a-input
v-model="formData.address" v-model="formData.address"
:placeholder="$t('user.info.address.placeholder')" :placeholder="$t('user.info.address.placeholder')"
/> />
</a-form-item> </a-form-item>
<a-form-item
field="code"
:label="$t('user.info.code')"
:rules="[
{
required: true,
message: t('user.info.code.required'),
},
]"
:validate-trigger="['change', 'input']"
>
<a-input
v-model="formData.code"
:placeholder="$t('user.info.code.placeholder')"
/>
<a-button
type="primary"
style="margin-left: 50px; margin-right: 50px"
@click="getCodeData"
:disabled="flag"
>发送验证码
<div v-if="flag">({{ totalCount }}s)</div>
</a-button>
</a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
</template> </template>
@ -179,19 +207,22 @@ const formData = ref<CreateRecord>({
nickName: '', nickName: '',
password: '', password: '',
phone: '', phone: '',
email: '', email: '@qq.com',
enabled: 'true', enabled: 'true',
address: '', address: '',
deptId: 235, deptId: 235,
roleId: undefined, roleId: undefined,
permissionIds: [], permissionIds: [],
authorities: [], authorities: [],
code: '',
}); });
const flag = ref(false);
const totalCount = ref(60);
const deptOptions = ref(); const deptOptions = ref();
const getDeptData = async () => { const getDeptData = async () => {
const res = await deptList(); const res = await deptList();
deptOptions.value = res.data; deptOptions.value = res.data.records;
}; };
const loginConfig = useStorage('login-config', { const loginConfig = useStorage('login-config', {
@ -207,6 +238,7 @@ const userInfo = reactive({
password: loginConfig.value.password, password: loginConfig.value.password,
}); });
//
const handleSubmit = async ({ const handleSubmit = async ({
// //
errors, errors,
@ -253,23 +285,53 @@ const handleSubmit = async ({
} }
} }
}; };
//
const setRememberPassword = (value: boolean) => { const setRememberPassword = (value: boolean) => {
loginConfig.value.rememberPassword = value; loginConfig.value.rememberPassword = value;
}; };
//
const handleClick = () => { const handleClick = () => {
setVisible(true); setVisible(true);
}; };
//
const handleCancel = async () => { const handleCancel = async () => {
userCreateRef.value?.resetFields(); userCreateRef.value?.resetFields();
setVisible(false); setVisible(false);
}; };
//
const getCodeData = async () => {
if (formData.value.email === '') {
Message.error({
content: t('user.info.email.required'),
duration: 5 * 1000,
});
} else {
const res = await userStore.getCode(formData.value.email);
if (res.status === 200) {
flag.value = true;
setInterval(() => {
if (totalCount.value !== 0) {
totalCount.value = totalCount.value * 1 - 1;
}
}, 1000);
setTimeout(() => {
flag.value = false;
totalCount.value = 60;
}, 60000);
}
}
};
//
const handleOk = async () => { const handleOk = async () => {
const valid = await userCreateRef.value?.validate(); const valid = await userCreateRef.value?.validate();
if (!valid) { if (!valid) {
const res = await userStore.createUser(formData.value); // formData.value.username = formData.value.email;
const res = await userStore.registerUser(formData.value);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
content: t('create.sucess'), content: t('create.sucess'),

View File

@ -1,81 +1,73 @@
<template> <template>
<div class="container"> <div class="container">
<div class="logo"> <div class="logo">
<img <div class="logo-text">Hello Ticket!</div>
alt="logo"
src="//p3-armor.byteimg.com/tos-cn-i-49unhts6dw/dfdba5317c0c20ce20e64fac803d52bc.svg~tplv-49unhts6dw-image.image"
/>
<div class="logo-text">Hello World!</div>
</div> </div>
<LoginBanner /> <LoginBanner />
<div class="content"> <div class="content">
<div class="content-inner"> <div class="content-inner">
<LoginForm /> <LoginForm />
</div> </div>
<div class="footer">
<Footer />
</div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import Footer from '@/components/footer/index.vue'; import LoginBanner from './components/banner.vue';
import LoginBanner from './components/banner.vue'; import LoginForm from './components/login-form.vue';
import LoginForm from './components/login-form.vue';
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.container { .container {
display: flex;
height: 100vh;
.banner {
width: 550px;
background: linear-gradient(163.85deg, #1d2129 0%, #00308f 100%);
}
.content {
position: relative;
display: flex; display: flex;
height: 100vh; flex: 1;
.banner {
width: 550px;
background: linear-gradient(163.85deg, #1d2129 0%, #00308f 100%);
}
.content {
position: relative;
display: flex;
flex: 1;
align-items: center;
justify-content: center;
padding-bottom: 40px;
}
.footer {
position: absolute;
right: 0;
bottom: 0;
width: 100%;
}
}
.logo {
position: fixed;
top: 24px;
left: 22px;
z-index: 1;
display: inline-flex;
align-items: center; align-items: center;
justify-content: center;
&-text { padding-bottom: 40px;
margin-right: 4px;
margin-left: 4px;
color: var(--color-fill-1);
font-size: 20px;
}
} }
.footer {
position: absolute;
right: 0;
bottom: 0;
width: 100%;
}
}
.logo {
position: fixed;
top: 24px;
left: 22px;
z-index: 1;
display: inline-flex;
align-items: center;
&-text {
margin-right: 4px;
margin-left: 4px;
color: var(--color-fill-1);
font-size: 20px;
}
}
</style> </style>
<style lang="less" scoped> <style lang="less" scoped>
// responsive // responsive
@media (max-width: @screen-lg) { @media (max-width: @screen-lg) {
.container { .container {
.banner { .banner {
width: 25%; width: 25%;
}
} }
} }
}
</style> </style>

View File

@ -1,11 +1,11 @@
export default { export default {
'login.form.title': 'ZSC Learning Platform', 'login.form.title': 'ZSC Learning Platform',
'login.form.userName.errMsg': 'Username cannot be empty', 'login.form.userName.errMsg': 'Username cannot be empty(Email)',
'login.form.password.errMsg': 'Password cannot be empty', 'login.form.password.errMsg': 'Password cannot be empty',
'login.form.login.errMsg': 'Login error, refresh and try again', 'login.form.login.errMsg': 'Login error, refresh and try again',
'login.form.login.success': 'welcome to use', 'login.form.login.success': 'welcome to use',
'login.form.userName.placeholder': 'Username: admin', 'login.form.userName.placeholder': 'Username(Email)',
'login.form.password.placeholder': 'Password: admin', 'login.form.password.placeholder': 'Password',
'login.form.rememberPassword': 'Remember password', 'login.form.rememberPassword': 'Remember password',
'login.form.forgetPassword': 'Forgot password', 'login.form.forgetPassword': 'Forgot password',
'login.form.login': 'login', 'login.form.login': 'login',

View File

@ -1,6 +1,6 @@
export default { export default {
'login.form.title': '中山学院学习平台', 'login.form.title': '票据管理系统',
'login.form.userName.errMsg': '用户名不能为空', 'login.form.userName.errMsg': '账号不能为空',
'login.form.password.errMsg': '密码不能为空', 'login.form.password.errMsg': '密码不能为空',
'login.form.login.errMsg': '登录出错,请刷新重试', 'login.form.login.errMsg': '登录出错,请刷新重试',
'login.form.login.success': '欢迎使用', 'login.form.login.success': '欢迎使用',
@ -10,11 +10,15 @@ export default {
'login.form.forgetPassword': '忘记密码', 'login.form.forgetPassword': '忘记密码',
'login.form.login': '登录', 'login.form.login': '登录',
'login.form.register': '注册账号', 'login.form.register': '注册账号',
'login.banner.slogan1': '开箱即用的高质量模板',
'login.banner.subSlogan1': '丰富的的页面模板,覆盖大多数典型业务场景', 'login.banner.slogan1': '数据查询',
'login.banner.slogan2': '内置了常见问题的解决方案', 'login.banner.subSlogan1': '用户可以通过关键词、条件组合等方式进行快速查询和检索',
'login.banner.subSlogan2': '国际化,路由配置,状态管理应有尽有', 'login.banner.slogan2': '数据存储',
'login.banner.slogan3': '接入可视化增强工具AUX', 'login.banner.subSlogan2': '系统具备强大的数据存储功能,能够将海量数据存储在数据库中',
'login.banner.subSlogan3': '实现灵活的区块式开发', 'login.banner.slogan3': '数据输出',
'create.user': '注册用户' 'login.banner.subSlogan3': '系统可以将查询和分析结果以表格和图表导出',
'create.user': '注册用户',
'user.info.emailCode.placeholder':'请输入Email(用于获取验证码)',
'user.info.email.required':'Email不能为空',
}; };

View File

@ -1,25 +1,26 @@
<template> <template>
<a-button v-if="props.isCreate" type="primary" @click="handleClick"> <a-button v-if="props.isCreate" type="primary" @click="handleClick">
<template #icon><icon-plus /></template> <template #icon><icon-plus /></template>
{{ modalTitle }} {{ t('create') }}
</a-button> </a-button>
<a-button <a-button
v-if="!props.isCreate" v-if="!props.isCreate"
type="outline" type="outline"
size="small" size="small"
:style="{ marginRight: '10px' }" :style="{ marginRight: '10px', padding: '7px' }"
@click="handleClick" @click="handleClick"
> >
{{ modalTitle }} <template #icon><icon-edit /></template>
{{ t('edit') }}
</a-button> </a-button>
<a-modal <a-modal
width="600px" width="700px"
:visible="visible" :visible="visible"
@ok="handleSubmit" @ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
> >
<template #title>{{ modalTitle }}</template> <template #title>{{ modalTitle }}</template>
<a-form ref="createEditRef" :model="formData" :style="{ width: '500px' }"> <a-form ref="createEditRef" :model="formData" :style="{ width: '650px' }">
<a-form-item <a-form-item
field="name" field="name"
:label="$t('dept.info.name')" :label="$t('dept.info.name')"
@ -35,6 +36,7 @@
<a-textarea <a-textarea
v-model="formData.remark" v-model="formData.remark"
:placeholder="$t('dept.info.remark.placeholder')" :placeholder="$t('dept.info.remark.placeholder')"
style="height: 100px"
/> />
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -58,7 +60,7 @@ const props = defineProps({
}); });
const { t } = useI18n(); const { t } = useI18n();
const modalTitle = computed(() => { const modalTitle = computed(() => {
return props.isCreate ? t('create') : t('edit'); return props.isCreate ? t('createDept') : t('editDept');
}); });
const { visible, setVisible } = useVisible(false); const { visible, setVisible } = useVisible(false);
const deptStore = useDeptStore(); const deptStore = useDeptStore();

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="container"> <div class="container">
<Breadcrumb :items="['menu.system', 'menu.system.dept']" /> <Breadcrumb :items="['menu.system', 'menu.system.dept']" />
<a-card class="general-card" :title="$t('menu.list.searchTable')"> <a-card class="general-card" title=" ">
<a-row> <a-row>
<a-col :flex="1"> <a-col :flex="1">
<a-form <a-form
:model="formModel" :model="formModel"
:label-col-props="{ span: 6 }" :label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 18 }" :wrapper-col-props="{ span: 12 }"
label-align="right" label-align="right"
> >
<a-row :gutter="16"> <a-row :gutter="16">
@ -118,6 +118,8 @@
:data="renderData" :data="renderData"
:bordered="false" :bordered="false"
:size="size" :size="size"
:pagination="false"
style="margin-bottom: 40px"
> >
<template #index="{ rowIndex }"> <template #index="{ rowIndex }">
{{ rowIndex + 1 }} {{ rowIndex + 1 }}
@ -146,12 +148,28 @@
type="error" type="error"
@ok="handleDelete(record)" @ok="handleDelete(record)"
> >
<a-button type="primary" size="small" status="danger"> <a-button
type="primary"
size="small"
status="danger"
style="padding: 7px"
>
<template #icon><icon-delete /></template>
{{ $t('delete') }} {{ $t('delete') }}
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
</template> </template>
</a-table> </a-table>
<a-pagination
style="float: right; position: relative; right: 1px; bottom: 25px"
:total="pagination.total"
@page-size-change="onSizeChange"
:size="size"
@change="onPageChange"
show-total
show-jumper
show-page-size
/>
</a-card> </a-card>
</div> </div>
</template> </template>
@ -161,6 +179,7 @@ import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import useTableOption from '@/hooks/table-option'; import useTableOption from '@/hooks/table-option';
import { Pagination } from '@/types/global';
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
@ -187,6 +206,12 @@ const generateFormModel = () => {
code: '', code: '',
}; };
}; };
const pagination: Pagination = {
page: 1,
size: 10,
current: 1,
total: 10,
};
const { t } = useI18n(); const { t } = useI18n();
const renderData = ref<DeptRecord[]>([]); const renderData = ref<DeptRecord[]>([]);
@ -202,10 +227,16 @@ const columns = computed<TableColumnData[]>(() => [
{ {
title: t('deptTable.columns.name'), title: t('deptTable.columns.name'),
dataIndex: 'name', dataIndex: 'name',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('deptTable.columns.id'), title: t('deptTable.columns.id'),
dataIndex: 'id', dataIndex: 'id',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('deptTable.columns.remark'), title: t('deptTable.columns.remark'),
@ -215,6 +246,9 @@ const columns = computed<TableColumnData[]>(() => [
title: t('deptTable.columns.createTime'), title: t('deptTable.columns.createTime'),
dataIndex: 'createTime', dataIndex: 'createTime',
slotName: 'createTime', slotName: 'createTime',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('deptTable.columns.enabled'), title: t('deptTable.columns.enabled'),
@ -233,8 +267,11 @@ const fetchData = async (params?: Partial<DeptRecord>) => {
setLoading(true); setLoading(true);
try { try {
const res = await deptStore.getDeptList(params); const res = await deptStore.getDeptList(params);
const { data } = res;
renderData.value = data; renderData.value = res.data.records;
pagination.page = res.data.page;
pagination.current = res.data.current;
pagination.total = res.data.total;
} catch (err) { } catch (err) {
// you can report use errorHandler or other // you can report use errorHandler or other
} finally { } finally {
@ -245,12 +282,26 @@ const fetchData = async (params?: Partial<DeptRecord>) => {
// //
const search = () => { const search = () => {
fetchData({ fetchData({
...pagination,
...formModel.value, ...formModel.value,
}); } as unknown as any);
}; };
search(); search();
//
const onPageChange = (current: number) => {
pagination.page = current;
pagination.current = current;
search();
};
//
const onSizeChange = (size: number) => {
pagination.size = size;
search();
};
// //
const reset = () => { const reset = () => {
formModel.value = generateFormModel(); formModel.value = generateFormModel();

View File

@ -21,5 +21,9 @@ export default {
'dept.info.remark': 'Remark', 'dept.info.remark': 'Remark',
'dept.info.remark.placeholder': 'Please enter Remark', 'dept.info.remark.placeholder': 'Please enter Remark',
'Confirm the deletion of this department' :'Confirm the deletion of this department?' 'Confirm the deletion of this department' :'Confirm the deletion of this department?',
// modalTitle
'createDept': 'Create Dept',
'editDept': 'Edit Dept Info'
}; };

View File

@ -5,8 +5,8 @@ export default {
'searchTable.form.deptName.placeholder': '请输入部门名称', 'searchTable.form.deptName.placeholder': '请输入部门名称',
'deptTable.columns.index': '序号', 'deptTable.columns.index': '序号',
'deptTable.columns.name': '区域名称', 'deptTable.columns.name': '部门名称',
'deptTable.columns.id': '区域ID', 'deptTable.columns.id': '部门ID',
'deptTable.columns.remark': '备注', 'deptTable.columns.remark': '备注',
'deptTable.columns.enabled': '是否启用', 'deptTable.columns.enabled': '是否启用',
'deptTable.columns.createTime': '创建时间', 'deptTable.columns.createTime': '创建时间',
@ -21,5 +21,10 @@ export default {
'dept.info.remark': '备注', 'dept.info.remark': '备注',
'dept.info.remark.placeholder': '请输入备注', 'dept.info.remark.placeholder': '请输入备注',
'Confirm the deletion of this department' :'确认删除此部门?' 'Confirm the deletion of this department' :'确认删除此部门?',
// modalTitle
'createDept': '新增部门',
'editDept': '修改部门信息'
}; };

View File

@ -1,25 +1,26 @@
<template> <template>
<a-button v-if="props.isCreate" type="primary" @click="handleClick"> <a-button v-if="props.isCreate" type="primary" @click="handleClick">
<template #icon><icon-plus /></template> <template #icon><icon-plus /></template>
{{ modalTitle }} {{ t('create') }}
</a-button> </a-button>
<a-button <a-button
v-if="!props.isCreate" v-if="!props.isCreate"
type="outline" type="outline"
size="small" size="small"
:style="{ marginRight: '10px' }" :style="{ marginRight: '10px', padding: '7px' }"
@click="handleClick" @click="handleClick"
> >
{{ modalTitle }} <template #icon><icon-edit /></template>
{{ t('edit') }}
</a-button> </a-button>
<a-modal <a-modal
width="600px" width="700px"
:visible="visible" :visible="visible"
@ok="handleSubmit" @ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
> >
<template #title>{{ modalTitle }}</template> <template #title>{{ modalTitle }}</template>
<a-form ref="createEditRef" :model="formData" :style="{ width: '500px' }"> <a-form ref="createEditRef" :model="formData" :style="{ width: '650px' }">
<a-form-item <a-form-item
field="name" field="name"
:label="$t('role.info.name')" :label="$t('role.info.name')"
@ -35,7 +36,9 @@
<a-form-item field="remark" :label="$t('role.info.remark')"> <a-form-item field="remark" :label="$t('role.info.remark')">
<a-textarea <a-textarea
v-model="formData.remark" v-model="formData.remark"
:show-word-limit="true"
:placeholder="$t('role.info.remark.placeholder')" :placeholder="$t('role.info.remark.placeholder')"
style="height: 100px"
/> />
</a-form-item> </a-form-item>
</a-form> </a-form>
@ -60,7 +63,7 @@ const props = defineProps({
}); });
const { t } = useI18n(); const { t } = useI18n();
const modalTitle = computed(() => { const modalTitle = computed(() => {
return props.isCreate ? t('create') : t('edit'); return props.isCreate ? t('createRole') : t('editRole');
}); });
const { visible, setVisible } = useVisible(false); const { visible, setVisible } = useVisible(false);
const createEditRef = ref<FormInstance>(); const createEditRef = ref<FormInstance>();

View File

@ -1,7 +1,46 @@
<template> <template>
<div class="container"> <div class="container">
<Breadcrumb :items="['menu.system', 'menu.system.role']" /> <Breadcrumb :items="['menu.system', 'menu.system.role']" />
<a-card class="general-card" :title="$t('menu.role.list')"> <a-card class="general-card" title=" ">
<a-row>
<a-col :flex="1">
<a-form
:model="formModel"
:label-col-props="{ span: 6 }"
:wrapper-col-props="{ span: 12 }"
label-align="right"
>
<a-row :gutter="16">
<a-col :span="12">
<a-form-item field="title" :label="$t('searchTable.form.name')">
<a-input
v-model="formModel.name"
:placeholder="$t('searchTable.form.name.placeholder')"
/>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-col>
<a-divider style="height: 42px" direction="vertical" />
<a-col :flex="'46px'" style="text-align: right">
<a-space :size="18">
<a-button type="primary" @click="search">
<template #icon>
<icon-search />
</template>
{{ $t('searchTable.form.search') }}
</a-button>
<a-button @click="reset">
<template #icon>
<icon-refresh />
</template>
{{ $t('searchTable.form.reset') }}
</a-button>
</a-space>
</a-col>
</a-row>
<a-divider style="margin-top: 10px" />
<a-row style="margin-bottom: 16px"> <a-row style="margin-bottom: 16px">
<a-col :span="12"> <a-col :span="12">
<a-space> <a-space>
@ -77,6 +116,7 @@
:bordered="false" :bordered="false"
:size="size" :size="size"
@page-change="onPageChange" @page-change="onPageChange"
style="margin-bottom: 40px"
> >
<template #index="{ rowIndex }"> <template #index="{ rowIndex }">
{{ rowIndex + 1 }} {{ rowIndex + 1 }}
@ -105,12 +145,28 @@
type="error" type="error"
@ok="handleDelete(record)" @ok="handleDelete(record)"
> >
<a-button type="primary" size="small" status="danger"> <a-button
type="primary"
size="small"
status="danger"
style="padding: 7px"
>
<template #icon><icon-delete /></template>
{{ $t('delete') }} {{ $t('delete') }}
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
</template> </template>
</a-table> </a-table>
<a-pagination
style="float: right; position: relative; right: 1px; bottom: 25px"
:total="pagination.total"
@page-size-change="onSizeChange"
:size="size"
@change="onPageChange"
show-total
show-jumper
show-page-size
/>
</a-card> </a-card>
</div> </div>
</template> </template>
@ -119,6 +175,7 @@
import { computed, ref, reactive, watch, nextTick } from 'vue'; import { computed, ref, reactive, watch, nextTick } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import { Pagination } from '@/types/global';
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import useTableOption from '@/hooks/table-option'; import useTableOption from '@/hooks/table-option';
@ -143,6 +200,19 @@ const {
deepClone, deepClone,
} = useTableOption(); } = useTableOption();
const roleStore = useRoleStore(); const roleStore = useRoleStore();
const generateFormModel = () => {
return {
name: undefined,
};
};
const formModel = ref(generateFormModel());
const pagination: Pagination = {
page: 1,
size: 10,
current: 1,
total: 10,
};
// //
const columns = computed<TableColumnData[]>(() => [ const columns = computed<TableColumnData[]>(() => [
@ -154,19 +224,21 @@ const columns = computed<TableColumnData[]>(() => [
{ {
title: t('roleTable.columns.name'), title: t('roleTable.columns.name'),
dataIndex: 'name', dataIndex: 'name',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('roleTable.columns.remark'), title: t('roleTable.columns.remark'),
dataIndex: 'remark', dataIndex: 'remark',
}, },
// {
// title: t('roleTable.columns.authorities'),
// dataIndex: 'authorities',
// },
{ {
title: t('roleTable.columns.createTime'), title: t('roleTable.columns.createTime'),
dataIndex: 'createTime', dataIndex: 'createTime',
slotName: 'createTime', slotName: 'createTime',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('roleTable.columns.enabled'), title: t('roleTable.columns.enabled'),
@ -181,11 +253,15 @@ const columns = computed<TableColumnData[]>(() => [
]); ]);
// //
const fetchData = async () => { const fetchData = async (params: any) => {
setLoading(true); setLoading(true);
try { try {
const res = await roleStore.getRoleList(); const res = await roleStore.getRoleList(params);
renderData.value = res.data; console.log('info', res.data);
renderData.value = res.data.records;
pagination.page = res.data.page;
pagination.current = res.data.current;
pagination.total = res.data.total;
} catch (err) { } catch (err) {
// you can report use errorHandler or other // you can report use errorHandler or other
} finally { } finally {
@ -193,7 +269,33 @@ const fetchData = async () => {
} }
}; };
fetchData(); //
const search = () => {
fetchData({
...pagination,
...formModel.value,
} as unknown as any);
};
search();
//
const onPageChange = (current: number) => {
pagination.page = current;
pagination.current = current;
search();
};
//
const onSizeChange = (size: number) => {
pagination.size = size;
search();
};
//
const reset = () => {
formModel.value = generateFormModel();
};
// //
const enabledStatus = async (record: string) => { const enabledStatus = async (record: string) => {
@ -220,7 +322,7 @@ const handleDelete = async (record: RoleRecord) => {
content: t('delete.role.sucess'), content: t('delete.role.sucess'),
duration: 5 * 1000, duration: 5 * 1000,
}); });
fetchData(); search();
} else { } else {
Message.error({ Message.error({
content: t('delete.role.fail'), content: t('delete.role.fail'),

View File

@ -14,6 +14,9 @@ export default {
'delete.role.sucess': 'Delete Role Sucess', 'delete.role.sucess': 'Delete Role Sucess',
'delete.role.fail': 'Delete Role Fail', 'delete.role.fail': 'Delete Role Fail',
'searchTable.form.name':'Name',
'searchTable.form.name.placeholder':'Please enter Name',
'role.info.name': 'Name', 'role.info.name': 'Name',
'role.info.name.placeholder': 'Please enter Name', 'role.info.name.placeholder': 'Please enter Name',
'role.info.name.required': 'Name is required', 'role.info.name.required': 'Name is required',
@ -27,4 +30,9 @@ export default {
'modify.status.sucess':'Modify status sucess', 'modify.status.sucess':'Modify status sucess',
'modify.status.fail':'Modify status fail', 'modify.status.fail':'Modify status fail',
// modalTitle
'createRole': 'Create Role',
'editRole': 'Edit Role Info',
}; };

View File

@ -14,6 +14,9 @@ export default {
'delete.role.sucess': '删除角色成功', 'delete.role.sucess': '删除角色成功',
'delete.role.fail': '删除角色失败', 'delete.role.fail': '删除角色失败',
'searchTable.form.name':'角色名称',
'searchTable.form.name.placeholder':'请输入角色名称',
'role.info.name': '角色名称', 'role.info.name': '角色名称',
'role.info.name.placeholder': '请输入角色名称', 'role.info.name.placeholder': '请输入角色名称',
'role.info.name.required': '角色不能为空', 'role.info.name.required': '角色不能为空',
@ -27,4 +30,9 @@ export default {
'modify.status.sucess':'修改状态成功', 'modify.status.sucess':'修改状态成功',
'modify.status.fail':'修改状态失败', 'modify.status.fail':'修改状态失败',
// modalTitle
'createRole': '新增角色',
'editRole': '修改角色信息'
}; };

View File

@ -1,26 +1,27 @@
<template> <template>
<a-button v-if="props.isCreate" type="primary" @click="handleClick"> <a-button v-if="props.isCreate" type="primary" @click="handleClick">
<template #icon><icon-plus /></template> <template #icon><icon-plus /></template>
{{ modalTitle }} {{ t('create') }}
</a-button> </a-button>
<a-button <a-button
v-if="!props.isCreate" v-if="!props.isCreate"
type="outline" type="outline"
size="small" size="small"
:style="{ marginRight: '10px' }" :style="{ marginRight: '10px', padding: '7px' }"
@click="handleClick" @click="handleClick"
> >
{{ modalTitle }} <template #icon><icon-edit /></template>
{{ t('edit') }}
</a-button> </a-button>
<a-modal <a-modal
width="600px" width="700px"
:visible="visible" :visible="visible"
@ok="handleSubmit" @ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
> >
<template #title>{{ modalTitle }}</template> <template #title>{{ modalTitle }}</template>
<a-form ref="CreateRef" :model="formData" :style="{ width: '500px' }"> <a-form ref="CreateRef" :model="formData" :style="{ width: '650px' }">
<a-form-item <a-form-item
field="username" field="username"
:label="$t('user.info.username')" :label="$t('user.info.username')"
@ -42,21 +43,20 @@
<div v-else>{{ formData.username }}</div> <div v-else>{{ formData.username }}</div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
field="password" field="email"
:label="$t('user.info.password')" :label="$t('user.info.email')"
v-if="isCreate" :rules="[
{
required: true,
type: 'email',
message: t('user.info.email.required'),
},
]"
:validate-trigger="['change', 'input']" :validate-trigger="['change', 'input']"
:rules="[{ required: true, message: t('user.info.password.required') }]"
> >
<a-input <a-input
v-model="formData.password" v-model="formData.email"
:placeholder="$t('user.info.password.placeholder')" :placeholder="$t('user.info.email.placeholder')"
/>
</a-form-item>
<a-form-item field="nickName" :label="$t('user.info.nickName')">
<a-input
v-model="formData.nickName"
:placeholder="$t('user.info.nickName.placeholder')"
/> />
</a-form-item> </a-form-item>
<a-form-item <a-form-item
@ -74,22 +74,24 @@
/> />
</a-form-item> </a-form-item>
<a-form-item <a-form-item
field="email" field="password"
:label="$t('user.info.email')" :label="$t('user.info.password')"
:rules="[ v-if="isCreate"
{
required: true,
type: 'email',
message: t('user.info.email.required'),
},
]"
:validate-trigger="['change', 'input']" :validate-trigger="['change', 'input']"
:rules="[{ required: true, message: t('user.info.password.required') }]"
> >
<a-input <a-input
v-model="formData.email" v-model="formData.password"
:placeholder="$t('user.info.email.placeholder')" :placeholder="$t('user.info.password.placeholder')"
/> />
</a-form-item> </a-form-item>
<a-form-item field="nickName" :label="$t('user.info.nickName')">
<a-input
v-model="formData.nickName"
:placeholder="$t('user.info.nickName.placeholder')"
/>
</a-form-item>
<a-form-item field="address" :label="$t('user.info.address')"> <a-form-item field="address" :label="$t('user.info.address')">
<a-input <a-input
v-model="formData.address" v-model="formData.address"
@ -139,11 +141,10 @@
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import { computed, PropType, ref } from 'vue'; import { computed, PropType, ref } from 'vue';
import { CreateRecord, UserRecord } from '@/api/user'; import { CreateRecord } from '@/api/user';
import { FormInstance } from '@arco-design/web-vue/es/form'; import { FormInstance } from '@arco-design/web-vue/es/form';
import { queryRoleList } from '@/api/role'; import { queryRoleList } from '@/api/role';
import { deptList } from '@/api/dept'; import { deptList } from '@/api/dept';
import { computedAsync } from '@vueuse/core';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
@ -155,7 +156,7 @@ const props = defineProps({
}); });
const { t } = useI18n(); const { t } = useI18n();
const modalTitle = computed(() => { const modalTitle = computed(() => {
return props.isCreate ? t('create') : t('edit'); return props.isCreate ? t('createUser') : t('editUser');
}); });
const { visible, setVisible } = useVisible(false); const { visible, setVisible } = useVisible(false);
const checkKeys = ref<number[]>([]); const checkKeys = ref<number[]>([]);
@ -180,32 +181,16 @@ let formDifer = {};
const userStore = useUserStore(); const userStore = useUserStore();
// //
// const deptOptions = computedAsync(async () => {
// const { data } = await deptList();
// const deptData = data.filter((item: any) => {
// return item.enabled !== false;
// });
// return deptData;
// });
const deptOptions = ref(); const deptOptions = ref();
const getDeptData = async () => { const getDeptData = async () => {
const res = await deptList(); const res = await deptList();
deptOptions.value = res.data; deptOptions.value = res.data.records;
}; };
// //
// const roleOptions = computedAsync(async () => {
// const res = await queryRoleList();
// const roleData = res.data.filter((item: any) => {
// return item.enabled !== false;
// });
// return roleData;
// });
const roleOptions = ref(); const roleOptions = ref();
const getRoleData = async () => { const getRoleData = async () => {
const res = await queryRoleList(); const res = await queryRoleList('');
roleOptions.value = res.data.filter((item: any) => { roleOptions.value = res.data.records.filter((item: any) => {
return item.enabled !== false; return item.enabled !== false;
}); });
}; };
@ -215,7 +200,6 @@ const handleClick = () => {
getDeptData(); getDeptData();
getRoleData(); getRoleData();
const userId = props.prem?.id; const userId = props.prem?.id;
// //
if (!props.isCreate && userId) { if (!props.isCreate && userId) {
formData.value = props.prem; formData.value = props.prem;
@ -242,6 +226,7 @@ const handleSubmit = async () => {
formData.value.permissionIds = checkKeys.value; formData.value.permissionIds = checkKeys.value;
// //
if (props.isCreate) { if (props.isCreate) {
// formData.value.username = formData.value.email;
const res = await userStore.createUser(formData.value); const res = await userStore.createUser(formData.value);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="container"> <div class="container">
<Breadcrumb :items="['menu.system', 'menu.system.user']" /> <Breadcrumb :items="['menu.system', 'menu.system.user']" />
<a-card class="general-card" :title="$t('menu.list.searchTable')"> <a-card class="general-card" title=" ">
<a-row> <a-row>
<a-col :flex="1"> <a-col :flex="1">
<a-form <a-form
@ -80,15 +80,26 @@
</a-row> </a-row>
<a-divider style="margin-top: 0" /> <a-divider style="margin-top: 0" />
<a-row style="margin-bottom: 16px"> <a-row>
<a-col :span="12"> <a-col :span="12">
<a-space> <a-space>
<UserEdit ref="createUserRef" :is-create="true" @refresh="search" /> <UserEdit ref="createUserRef" :is-create="true" @refresh="search" />
</a-space> </a-space>
<a-button @click="generateExcel" style="margin-left: 20px">
<template #icon>
<icon-download size="18" />
</template>
{{ $t('searchTable.operation.download') }}
</a-button>
</a-col> </a-col>
<a-col <a-col
:span="12" :span="12"
style="display: flex; align-items: center; justify-content: end" style="
display: flex;
align-items: center;
justify-content: end;
padding-bottom: 20px;
"
> >
<a-tooltip :content="$t('searchTable.actions.refresh')"> <a-tooltip :content="$t('searchTable.actions.refresh')">
<div class="action-icon" @click="search"> <div class="action-icon" @click="search">
@ -152,12 +163,15 @@
<a-table <a-table
row-key="id" row-key="id"
:loading="loading" :loading="loading"
:pagination="pagination" :pagination="false"
:columns="(cloneColumns as TableColumnData[])" :columns="(cloneColumns as TableColumnData[])"
:data="renderData" :data="renderData"
:bordered="false" :bordered="false"
:size="size" :size="size"
@page-change="onPageChange" @page-change="onPageChange"
style="margin-bottom: 40px"
:filter-icon-align-left="alignLeft"
@change="handleSortChange"
> >
<template #index="{ rowIndex }"> <template #index="{ rowIndex }">
{{ rowIndex + 1 + (pagination.current - 1) * pagination.size }} {{ rowIndex + 1 + (pagination.current - 1) * pagination.size }}
@ -177,12 +191,12 @@
:is-create="false" :is-create="false"
@refresh="fetchData" @refresh="fetchData"
/> />
<!-- <Userc <Userc
ref="editUserRef" ref="editUserRef"
:user="record" :user="record"
:is-create="false" :is-create="false"
@refresh="search" @refresh="search"
/> --> />
<!-- <a-popconfirm <!-- <a-popconfirm
content="确认删除此用户?" content="确认删除此用户?"
type="error" type="error"
@ -194,22 +208,31 @@
</a-popconfirm> --> </a-popconfirm> -->
</template> </template>
</a-table> </a-table>
<a-pagination
style="float: right; position: relative; right: 1px; bottom: 25px"
:total="pagination.total"
@page-size-change="onSizeChange"
:size="size"
@change="onPageChange"
show-total
show-jumper
show-page-size
/>
</a-card> </a-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, reactive, watch, nextTick } from 'vue'; import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import { UserRecord, UserParams } from '@/api/user'; import { UserRecord, UserParams } from '@/api/user';
import { Pagination } from '@/types/global'; import { Pagination } from '@/types/global';
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface'; import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
import cloneDeep from 'lodash/cloneDeep';
import Sortable from 'sortablejs';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { downloadExcel, DownloadExcelPrams } from '@/utils/excel';
import useTableOption from '@/hooks/table-option'; import useTableOption from '@/hooks/table-option';
import UserEdit from './components/user-edit.vue'; import UserEdit from './components/user-edit.vue';
@ -245,13 +268,12 @@ const {
const userStore = useUserStore(); const userStore = useUserStore();
const basePagination: Pagination = { const pagination: Pagination = {
page: 1, page: 1,
size: 10, size: 10,
current: 1,
total: null,
}; };
const pagination = reactive({
...basePagination,
});
const columns = computed<TableColumnData[]>(() => [ const columns = computed<TableColumnData[]>(() => [
{ {
@ -263,18 +285,30 @@ const columns = computed<TableColumnData[]>(() => [
{ {
title: t('userTable.columns.username'), title: t('userTable.columns.username'),
dataIndex: 'username', dataIndex: 'username',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('userTable.columns.phone'), title: t('userTable.columns.phone'),
dataIndex: 'phone', dataIndex: 'phone',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('userTable.columns.email'), title: t('userTable.columns.email'),
dataIndex: 'email', dataIndex: 'email',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('userTable.columns.nickName'), title: t('userTable.columns.nickName'),
dataIndex: 'nickName', dataIndex: 'nickName',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('userTable.columns.address'), title: t('userTable.columns.address'),
@ -311,9 +345,10 @@ const fetchData = async (
try { try {
const res = await userStore.getUserList(params); const res = await userStore.getUserList(params);
renderData.value = res.data.records; renderData.value = res.data.records;
pagination.page = res.data.page;
pagination.current = res.data.current; pagination.current = res.data.current;
pagination.total = res.data.total; pagination.total = res.data.total;
// pagination.size = data.size; pagination.size = res.data.size;
} catch (err) { } catch (err) {
// you can report use errorHandler or other // you can report use errorHandler or other
} finally { } finally {
@ -321,26 +356,58 @@ const fetchData = async (
} }
}; };
//
const generateExcel = () => {
const param: DownloadExcelPrams = {
columns: [
{ title: '用户名', key: 'username' },
{ title: '昵称', key: 'nickName' },
{ title: '电话号码', key: 'phone' },
{ title: '部门Id', key: 'deptId' },
{ title: '角色Id', key: 'roleId' },
{ title: 'email', key: 'email' },
{ title: '启用状态', key: 'enabled' },
],
rows: renderData.value,
name: '用户表格',
};
downloadExcel(param);
};
// //
const search = () => { const search = () => {
fetchData({ fetchData({
...basePagination, ...pagination,
...formModel.value, ...formModel.value,
} as unknown as UserParams); } as unknown as UserParams);
}; };
// //
const onPageChange = (current: number) => { const onPageChange = (current: number) => {
fetchData({ ...basePagination, current }); pagination.page = current;
pagination.current = current;
search();
}; };
fetchData(); //
const onSizeChange = (size: number) => {
pagination.size = size;
search();
};
search();
// //
const reset = () => { const reset = () => {
formModel.value = generateFormModel(); formModel.value = generateFormModel();
}; };
//
const alignLeft = ref(false);
const handleSortChange = (data: any, extra: any, currentDataSource: any) => {
console.log('change', data, extra, currentDataSource);
};
// //
const enabledStatus = async (record: string) => { const enabledStatus = async (record: string) => {
record.enabled = !record.enabled; record.enabled = !record.enabled;

View File

@ -65,4 +65,8 @@ export default {
'user.info.role.placeholder': 'Please select Role', 'user.info.role.placeholder': 'Please select Role',
'user.info.role.required': 'Role is required', 'user.info.role.required': 'Role is required',
// modalTitle
'createUser': 'Create User',
'editUser': 'Edit User Info'
}; };

View File

@ -16,7 +16,7 @@ export default {
'searchTable.form.selectDefault': '全部', 'searchTable.form.selectDefault': '全部',
'searchTable.operation.create': '新建', 'searchTable.operation.create': '新建',
'searchTable.operation.import': '批量导入', 'searchTable.operation.import': '批量导入',
'searchTable.operation.download': '下载', 'searchTable.operation.download': '导出',
// columns // columns
'userTable.columns.index': '序号', 'userTable.columns.index': '序号',
'userTable.columns.nickName': '昵称', 'userTable.columns.nickName': '昵称',
@ -64,5 +64,10 @@ export default {
'user.info.role':'角色', 'user.info.role':'角色',
'user.info.role.placeholder': '请选择角色', 'user.info.role.placeholder': '请选择角色',
'user.info.role.required': '请选择一个', 'user.info.role.required': '请选择一个',
'user.info.code':'验证码',
'user.info.code.placeholder':'请输入验证码',
// modalTitle
'createUser': '新增用户',
'editUser': '修改用户信息'
} }

View File

@ -1,33 +1,36 @@
<template> <template>
<!-- 新增 -->
<a-button <a-button
v-permission="['BILL_QUERY']" v-permission="['BILL_QUERY']"
v-if="props.isCreate" v-if="props.isCreate"
type="primary" type="primary"
size="small" size="small"
:style="{ marginRight: '10px' }"
@click="handleClick" @click="handleClick"
> >
<template #icon><icon-plus /></template>
{{ modalTitle }} {{ modalTitle }}
</a-button> </a-button>
<!-- 修改 -->
<a-button <a-button
v-permission="['BILL_UPDATE']" v-permission="['BILL_UPDATE']"
v-if="!props.isCreate" v-if="!props.isCreate"
size="small" size="small"
type="primary" type="primary"
:style="{ marginRight: '10px' }" :style="{ marginRight: '10px', padding: '7px' }"
@click="handleClick" @click="handleClick"
> >
<template #icon><icon-edit /></template>
{{ modalTitle }} {{ modalTitle }}
</a-button> </a-button>
<a-modal <a-modal
width="600px" width="700px"
:visible="visible" :visible="visible"
:footer="false" :footer="false"
@cancel="handleCancel" @cancel="handleCancel"
> >
<template #title>{{ modalTitle }}</template> <template #title>{{ modalTitle }}</template>
<a-form ref="createEditRef" :model="formData" :style="{ width: '500px' }"> <a-form ref="createEditRef" :model="formData" :style="{ width: '650px' }">
<a-form-item <a-form-item
field="companyName" field="companyName"
:label="$t('ticket.info.companyName')" :label="$t('ticket.info.companyName')"
@ -54,18 +57,6 @@
/> />
</a-form-item> </a-form-item>
<a-form-item
field="body"
:label="$t('ticket.info.body')"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: t('ticket.info.body.required') }]"
>
<a-textarea
v-model="formData.body"
:placeholder="$t('ticket.info.body.placeholder')"
/>
</a-form-item>
<a-form-item <a-form-item
field="money" field="money"
:label="$t('ticket.info.money')" :label="$t('ticket.info.money')"
@ -105,16 +96,6 @@
/> />
</a-form-item> </a-form-item>
<a-form-item field="attachment" :label="$t('ticket.info.attachment')">
<a-upload
:file-list="fileList"
:custom-request="Onchange"
:limit="1"
:on-before-remove="removeAttact"
v-model="formData.attachId"
/>
</a-form-item>
<a-form-item <a-form-item
field="deptId" field="deptId"
:label="$t('ticket.info.dept')" :label="$t('ticket.info.dept')"
@ -159,6 +140,29 @@
> >
{{ formData.comment }} {{ formData.comment }}
</a-form-item> </a-form-item>
<a-form-item
field="body"
:label="$t('ticket.info.body')"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: t('ticket.info.body.required') }]"
>
<a-textarea
v-model="formData.body"
:placeholder="$t('ticket.info.body.placeholder')"
style="height: 100px"
/>
</a-form-item>
<a-form-item field="attachment" :label="$t('ticket.info.attachment')">
<a-upload
:file-list="fileList"
:custom-request="Onchange"
:limit="5"
:on-before-remove="removeAttact"
/>
</a-form-item>
<a-form-item> <a-form-item>
<a-button type="dashed" @click="handleCancel"> <a-button type="dashed" @click="handleCancel">
{{ $t('cancel') }}</a-button {{ $t('cancel') }}</a-button
@ -182,10 +186,9 @@
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import { computed, PropType, ref } from 'vue'; import { computed, PropType, ref } from 'vue';
import { computedAsync } from '@vueuse/core';
import { FormInstance } from '@arco-design/web-vue/es/form'; import { FormInstance } from '@arco-design/web-vue/es/form';
import { SelectOptionData } from '@arco-design/web-vue/es/select/interface'; import { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import { Message } from '@arco-design/web-vue'; import { FileItem, Message } from '@arco-design/web-vue';
import { useTicketStore, useUserStore, useRoleStore } from '@/store'; import { useTicketStore, useUserStore, useRoleStore } from '@/store';
import { TicketRecord } from '@/api/ticket'; import { TicketRecord } from '@/api/ticket';
import { deptList } from '@/api/dept'; import { deptList } from '@/api/dept';
@ -216,7 +219,7 @@ const formData = ref<TicketRecord>({
type: '', type: '',
contactEmail: '', contactEmail: '',
companyName: '', companyName: '',
attachId: '', attachId: [],
auditorId: '', auditorId: '',
userId: undefined, userId: undefined,
submit: '', submit: '',
@ -228,7 +231,7 @@ const emit = defineEmits(['refresh']);
const deptOptions = ref(); const deptOptions = ref();
const getDeptData = async () => { const getDeptData = async () => {
const res = await deptList(); const res = await deptList();
deptOptions.value = res.data; deptOptions.value = res.data.records;
}; };
const typesOptions = computed<SelectOptionData[]>(() => [ const typesOptions = computed<SelectOptionData[]>(() => [
@ -250,8 +253,8 @@ let auiditRoleId = '';
// ID // ID
const getRoleId = async () => { const getRoleId = async () => {
const res = await roleStore.getRoleList(); const res = await roleStore.getRoleList('');
res.data.forEach((item: any) => { res.data.records.forEach((item: any) => {
if (item.name === 'auditor') { if (item.name === 'auditor') {
auiditRoleId = item.id; auiditRoleId = item.id;
} }
@ -277,12 +280,13 @@ const optionDept = async (flag: boolean) => {
let formDifer = {}; let formDifer = {};
const fileList = ref([]); const fileList = ref([]);
const attachList = ref([]);
// //
const Onchange = async (option: any) => { const Onchange = async (option: any) => {
const FormDatas = new FormData(); const FormDatas = new FormData();
FormDatas.append('file', option.fileItem.file); FormDatas.append('file', option.fileItem.file);
const res = await ticketStore.uploadFileTicket(FormDatas); const res = await ticketStore.uploadFile(FormDatas);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
content: t('upload.sucess'), content: t('upload.sucess'),
@ -290,7 +294,10 @@ const Onchange = async (option: any) => {
}); });
res.data.name = res.data.fileName; res.data.name = res.data.fileName;
fileList.value.push(res.data); fileList.value.push(res.data);
formData.value.attachId = res.data.id; console.log('res', res.data.id);
attachList.value.push(res.data.id);
formData.value.attachId = attachList;
console.log('poti', formData.value.attachId);
} else { } else {
Message.error({ Message.error({
content: t('upload.fail'), content: t('upload.fail'),
@ -300,8 +307,13 @@ const Onchange = async (option: any) => {
}; };
// //
const removeAttact = () => { const removeAttact = (fileItem: FileItem) => {
fileList.value.pop(); console.log('2', fileItem.id, fileList.value, attachList.value);
fileList.value = fileList.value.filter((item) => item.id !== fileItem.id);
attachList.value = attachList.value.filter((item) => item !== fileItem.id);
console.log('3', fileList.value, attachList.value);
formData.value.attachId = attachList.value;
console.log('3', formData.value.attachId);
}; };
// //
@ -317,12 +329,13 @@ const handleClick = () => {
// //
formData.value = res.data; formData.value = res.data;
// formData.value.attachId.forEach(async (item: any) => {
if (formData.value.attachId) { console.log('item,', item);
const data = await ticketStore.getAttachment(formData.value.attachId); const data = await ticketStore.getAttachment(item);
data.data.name = data.data.fileName; data.data.name = data.data.fileName;
fileList.value.push(data.data); fileList.value.push(data.data);
} });
attachList.value = formData.value.attachId;
// //
const auditInfo = await userStore.getUserDetail( const auditInfo = await userStore.getUserDetail(
formData.value.auditorId formData.value.auditorId
@ -389,7 +402,7 @@ const handleOk = async () => {
} }
} }
createEditRef.value?.resetFields(); createEditRef.value?.resetFields();
removeAttact(); attachList.value = [];
emit('refresh'); emit('refresh');
setVisible(false); setVisible(false);
} }
@ -430,7 +443,8 @@ const handleStorage = async () => {
} }
} }
createEditRef.value?.resetFields(); createEditRef.value?.resetFields();
removeAttact(); attachList.value = [];
fileList.value = [];
emit('refresh'); emit('refresh');
setVisible(false); setVisible(false);
} }
@ -438,7 +452,8 @@ const handleStorage = async () => {
// //
const handleCancel = async () => { const handleCancel = async () => {
removeAttact(); attachList.value = [];
fileList.value = [];
createEditRef.value?.resetFields(); createEditRef.value?.resetFields();
setVisible(false); setVisible(false);
}; };

View File

@ -1,33 +1,37 @@
<template> <template>
<!-- 详情 -->
<a-button <a-button
v-permission="['BILL_QUERY']" v-permission="['BILL_QUERY']"
v-if="props.isDetail" v-if="props.isDetail"
type="outline" type="outline"
size="small" size="small"
:style="{ marginRight: '10px' }" :style="{ marginRight: '10px', padding: '7px' }"
@click="handleClick" @click="handleClick"
> >
<template #icon><icon-eye /></template>
{{ modalTitle }} {{ modalTitle }}
</a-button> </a-button>
<!-- 审核 -->
<a-button <a-button
v-permission="['BILL_AUDIT']" v-permission="['BILL_AUDIT']"
v-if="!props.isDetail" v-if="!props.isDetail"
size="small" size="small"
type="primary" type="primary"
:style="{ marginRight: '10px' }" :style="{ marginRight: '10px', padding: '7px' }"
@click="handleClick" @click="handleClick"
> >
<template #icon><icon-search /></template>
{{ modalTitle }} {{ modalTitle }}
</a-button> </a-button>
<a-modal <a-modal
width="600px" width="700px"
:visible="visible" :visible="visible"
@ok="handleSubmit" @ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
> >
<template #title>{{ modalTitle }}</template> <template #title>{{ modalTitle }}</template>
<a-form ref="createEditRef" :model="formData" :style="{ width: '500px' }"> <a-form ref="createEditRef" :model="formData" :style="{ width: '650px' }">
<a-form-item field="companyName" :label="$t('ticket.info.companyName')"> <a-form-item field="companyName" :label="$t('ticket.info.companyName')">
<div> <div>
{{ formData.companyName }} {{ formData.companyName }}
@ -70,39 +74,16 @@
</div> </div>
</a-form-item> </a-form-item>
<a-form-item field="attachment" :label="$t('ticket.info.attachment')">
<a-upload
:file-list="fileList"
:custom-request="Onchange"
:limit="1"
:on-before-remove="removeAttact"
:show-remove-button="false"
v-model="formData.attachId"
/>
</a-form-item>
<a-form-item field="deptId" :label="$t('ticket.info.dept')"> <a-form-item field="deptId" :label="$t('ticket.info.dept')">
<a-tree-select {{ formData.deptName }}
v-model="formData.deptId"
:field-names="{
key: 'id',
title: 'name',
children: 'children',
}"
:data="deptOptions"
disabled
style="color: black"
/>
</a-form-item> </a-form-item>
<!-- 审核员 -->
<a-form-item field="auditorId" :label="$t('ticket.info.auditor')"> <a-form-item field="auditorId" :label="$t('ticket.info.auditor')">
<a-select v-model="formData.auditorId" disabled style="color: black"> {{ formData.auditor }}
<div v-for="(item, id) in auditorOptions" :key="id">
<a-option :value="item.id">{{ item.username }} </a-option>
</div>
</a-select>
</a-form-item> </a-form-item>
<!-- 审核状态 -->
<a-form-item <a-form-item
field="status" field="status"
:label="$t('ticket.info.status')" :label="$t('ticket.info.status')"
@ -112,7 +93,6 @@
{{ formData.status }} {{ formData.status }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
field="status" field="status"
v-else v-else
@ -121,18 +101,37 @@
:validate-trigger="['change', 'input']" :validate-trigger="['change', 'input']"
:rules="[{ required: true, message: t('ticket.info.status.required') }]" :rules="[{ required: true, message: t('ticket.info.status.required') }]"
> >
<a-radio-group <a-radio-group v-model="formData.submit" default-value="true">
v-for="i in statusOptions" <a-radio value="true">{{ t('pass') }}</a-radio>
:key="i.value" <a-radio value="false">{{ t('failed') }}</a-radio>
v-model="formData.submit"
>
<a-radio :value="i.value">{{ i.label }}</a-radio>
</a-radio-group> </a-radio-group>
</a-form-item> </a-form-item>
<a-form-item field="comment" :label="$t('ticket.info.comment')"> <!-- 审核意见 -->
<a-textarea v-model="formData.comment" v-if="!props.isDetail" /> <a-form-item
<div v-else>{{ formData.comment }} </div> field="comment"
:label="$t('ticket.info.comment')"
v-if="!props.isDetail"
>
<a-textarea v-model="formData.comment" style="height: 200px" />
</a-form-item>
<a-form-item field="comment" :label="$t('ticket.info.comment')" v-else>
<div>{{ formData.comment || '无' }}</div>
</a-form-item>
<!-- 附件 -->
<a-form-item field="attachment" :label="$t('ticket.info.attachment')">
<a-upload
:file-list="fileList"
:custom-request="Onchange"
:limit="5"
:disabled="true"
:on-before-remove="removeAttact"
:show-remove-button="false"
v-model="formData.attachId"
v-if="fileList.length !== 0"
/>
<div v-else></div>
</a-form-item> </a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
@ -142,7 +141,6 @@
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import { computed, PropType, ref } from 'vue'; import { computed, PropType, ref } from 'vue';
import { computedAsync } from '@vueuse/core';
import { FormInstance } from '@arco-design/web-vue/es/form'; import { FormInstance } from '@arco-design/web-vue/es/form';
import { SelectOptionData } from '@arco-design/web-vue/es/select/interface'; import { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
@ -178,8 +176,10 @@ const formData = ref<TicketRecord>({
companyName: '', companyName: '',
attachId: '', attachId: '',
auditorId: '', auditorId: '',
submit: undefined, submit: true,
comment: '', comment: '',
deptName: '',
auditor: '',
}); });
const emit = defineEmits(['refresh']); const emit = defineEmits(['refresh']);
@ -187,15 +187,15 @@ const emit = defineEmits(['refresh']);
const deptOptions = ref(); const deptOptions = ref();
const getDeptData = async () => { const getDeptData = async () => {
const res = await deptList(); const res = await deptList();
deptOptions.value = res.data; deptOptions.value = res.data.records;
}; };
let auiditRoleId = ''; let auiditRoleId = '';
// ID // ID
const getRoleId = async () => { const getRoleId = async () => {
const res = await roleStore.getRoleList(); const res = await roleStore.getRoleList('');
res.data.forEach((item: any) => { res.data.records.forEach((item: any) => {
if (item.name === 'auditor') { if (item.name === 'auditor') {
auiditRoleId = item.id; auiditRoleId = item.id;
} }
@ -216,17 +216,6 @@ const optionDept = async () => {
auditorOptions.value = res.data.records; auditorOptions.value = res.data.records;
}; };
const statusOptions = computed<SelectOptionData[]>(() => [
{
label: t('pass'),
value: true,
},
{
label: t('failed'),
value: false,
},
]);
const fileList = ref([]); const fileList = ref([]);
// //
const handleClick = async () => { const handleClick = async () => {
@ -237,18 +226,29 @@ const handleClick = async () => {
const res = await ticketStore.getDetail(ticketId); const res = await ticketStore.getDetail(ticketId);
// //
formData.value = res.data; formData.value = res.data;
formData.value.submit = 'true';
// //
if (formData.value.attachId) { formData.value.attachId.forEach(async (item: any) => {
const data = await ticketStore.getAttachment(formData.value.attachId); // console.log('item,', item);
const data = await ticketStore.getAttachment(item);
data.data.name = data.data.fileName; data.data.name = data.data.fileName;
fileList.value.push(data.data); fileList.value.push(data.data);
} });
// //
const auditInfo = await userStore.getUserDetail(formData.value.auditorId); const auditInfo = await userStore.getUserDetail(formData.value.auditorId);
formData.value.deptId = auditInfo.data.deptId; formData.value.deptId = auditInfo.data.deptId;
optionDept().then(() => { optionDept().then(() => {
deptOptions.value.forEach((item: any) => {
if (item.id === formData.value.deptId) {
formData.value.deptName = item.name;
}
});
auditorOptions.value.forEach((item: any) => {
if (item.id === formData.value.auditorId) {
formData.value.auditor = item.username;
}
});
setVisible(true); setVisible(true);
}); });
}; };
@ -271,7 +271,7 @@ const handleSubmit = async () => {
}); });
} }
} }
fileList.value.pop(); fileList.value = [];
emit('refresh'); emit('refresh');
setVisible(false); setVisible(false);
} }
@ -279,7 +279,7 @@ const handleSubmit = async () => {
// //
const handleCancel = async () => { const handleCancel = async () => {
fileList.value.pop(); fileList.value = [];
createEditRef.value?.resetFields(); createEditRef.value?.resetFields();
setVisible(false); setVisible(false);
}; };

View File

@ -1,7 +1,18 @@
<template> <template>
<div class="container"> <div class="container">
<Breadcrumb :items="['menu.ticket', 'menu.ticket.manage']" /> <Breadcrumb :items="['menu.ticket', 'menu.ticket.manage']" />
<a-card class="general-card" :title="$t('menu.list.searchTable')"> <a-card class="general-card">
<a-tabs default-active-key="PASS" @change="changeTop">
<a-tab-pane key="PASS" :title="t('pass')"> </a-tab-pane>
<a-tab-pane key="FAILED" :title="t('failed')"> </a-tab-pane>
<a-tab-pane key="EXAMINE" :title="t('unreviewed')"> </a-tab-pane>
<a-tab-pane
key="SUBMIT"
:title="t('drafts')"
v-if="userStore.permissions !== 'auditor'"
>
</a-tab-pane>
</a-tabs>
<a-row> <a-row>
<a-col :flex="1"> <a-col :flex="1">
<a-form <a-form
@ -11,6 +22,7 @@
label-align="right" label-align="right"
> >
<a-row :gutter="16"> <a-row :gutter="16">
<!-- 公司名称 -->
<a-col :span="8"> <a-col :span="8">
<a-form-item <a-form-item
field="companyName" field="companyName"
@ -25,6 +37,7 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- 标题 -->
<a-col :span="8"> <a-col :span="8">
<a-form-item <a-form-item
field="title" field="title"
@ -36,9 +49,42 @@
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- 用户 -->
<a-col :span="8">
<a-form-item
field="userName"
:label="$t('searchTable.form.userName')"
>
<a-input
v-model="formModel.userName"
:placeholder="$t('searchTable.form.committer.placeholder')"
/>
</a-form-item>
</a-col>
</a-row> </a-row>
<a-row :gutter="16"> <a-row :gutter="16">
<!-- 金额 -->
<a-col :span="8">
<a-form-item
field="money"
:label="$t('searchTable.form.money')"
>
<a-input-group>
<a-input
v-model="formModel.minMoney"
:placeholder="$t('searchTable.form.min')"
/>
<a-input
v-model="formModel.maxMoney"
:placeholder="$t('searchTable.form.max')"
/>
</a-input-group>
</a-form-item>
</a-col>
<!-- 票据类型 -->
<a-col :span="8"> <a-col :span="8">
<a-form-item field="type" :label="$t('searchTable.form.type')"> <a-form-item field="type" :label="$t('searchTable.form.type')">
<a-select <a-select
@ -49,15 +95,16 @@
</a-form-item> </a-form-item>
</a-col> </a-col>
<!-- 时间 -->
<a-col :span="8"> <a-col :span="8">
<a-form-item <a-form-item field="time" :label="$t('searchTable.form.time')">
field="status" <a-range-picker
:label="$t('searchTable.form.status')" v-model="formModel.time"
> style="width: 360px"
<a-select show-time
v-model="formModel.status" @select="onSelect"
:options="statusOptions" value-format="YYYY-MM-DD"
:placeholder="$t('searchTable.form.status.placeholder')" :allow-clear="false"
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
@ -88,12 +135,19 @@
<a-col :span="12"> <a-col :span="12">
<a-space> <a-space>
<TicketForm <TicketForm
v-if="formModel.status === 'SUBMIT'"
ref="createEditRef" ref="createEditRef"
:prem="ticketItem" :prem="formModel"
:is-create="true" :is-create="true"
@refresh="search" @refresh="search"
/> />
</a-space> </a-space>
<a-button @click="generateExcel" style="margin-left: 5px">
<template #icon>
<icon-download size="16" />
</template>
{{ $t('searchTable.operation.download') }}
</a-button>
</a-col> </a-col>
<a-col <a-col
:span="12" :span="12"
@ -162,10 +216,23 @@
:columns="(cloneColumns as TableColumnData[])" :columns="(cloneColumns as TableColumnData[])"
:data="renderData" :data="renderData"
:bordered="false" :bordered="false"
:pagination="pagination" :pagination="false"
:size="size" :size="size"
:expandable="expandable"
@page-change="onPageChange" @page-change="onPageChange"
style="margin-bottom: 40px"
> >
<template #expand-row="{ record }">
<!-- 下面展示子表格,根据需求对子table进行属性配置 -->
<a-descriptions layout="inline-horizontal" :column="1" bordered>
<descriptions-item :label="t('ticket.info.contactEmail')">
{{ record.contactEmail }}
</descriptions-item>
<descriptions-item :label="t('ticket.info.body')">
{{ record.body }}
</descriptions-item>
</a-descriptions>
</template>
<template #index="{ rowIndex }"> <template #index="{ rowIndex }">
{{ rowIndex + 1 + (pagination.current - 1) * pagination.size }} {{ rowIndex + 1 + (pagination.current - 1) * pagination.size }}
</template> </template>
@ -173,6 +240,21 @@
{{ dayjs(record.createTime).format('YYYY-MM-DD') }} {{ dayjs(record.createTime).format('YYYY-MM-DD') }}
</template> </template>
<template #status="{ record }">
<a-button status="success" v-if="record.status === '审核通过'">{{
record.status
}}</a-button>
<a-button status="danger" v-if="record.status === '审核未通过'">{{
record.status
}}</a-button>
<a-button status="normal" v-if="record.status === '待审核'">{{
record.status
}}</a-button>
<a-button status="warning" v-if="record.status === '待提交'">{{
record.status
}}</a-button>
</template>
<template #operations="{ record }"> <template #operations="{ record }">
<!-- 详情 --> <!-- 详情 -->
<TicketEdit <TicketEdit
@ -210,18 +292,33 @@
size="small" size="small"
status="danger" status="danger"
v-permission="['BILL_DELETE']" v-permission="['BILL_DELETE']"
v-if="
record.status === '待提交' || record.status === '审核未通过'
"
style="padding: 7px"
> >
<template #icon><icon-delete /></template>
{{ $t('delete') }} {{ $t('delete') }}
</a-button> </a-button>
</a-popconfirm> </a-popconfirm>
</template> </template>
</a-table> </a-table>
<a-pagination
style="float: right; position: relative; right: 1px; bottom: 25px"
:total="pagination.total"
@page-size-change="onSizeChange"
:size="size"
@change="onPageChange"
show-total
show-jumper
show-page-size
/>
</a-card> </a-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, watch, reactive } from 'vue'; import { computed, reactive, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import useTableOption from '@/hooks/table-option'; import useTableOption from '@/hooks/table-option';
@ -232,6 +329,7 @@ import dayjs from 'dayjs';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { useTicketStore, useUserStore } from '@/store'; import { useTicketStore, useUserStore } from '@/store';
import { TicketRecord } from '@/api/ticket'; import { TicketRecord } from '@/api/ticket';
import { downloadExcel, DownloadExcelPrams } from '@/utils/excel';
import TicketEdit from './components/ticket-edit.vue'; import TicketEdit from './components/ticket-edit.vue';
import TicketForm from './components/form-edit.vue'; import TicketForm from './components/form-edit.vue';
@ -255,13 +353,27 @@ const generateFormModel = () => {
companyName: '', companyName: '',
title: '', title: '',
type: '', type: '',
status: '', //
status: 'PASS',
time: [],
userName: '',
minMoney: '',
maxMoney: '',
startTime: '',
endTime: '',
}; };
}; };
const { t } = useI18n(); const { t } = useI18n();
const renderData = ref<TicketRecord[]>([]); const renderData = ref<TicketRecord[]>([]);
const formModel = ref(generateFormModel()); const formModel = ref(generateFormModel());
const currentStatus = ref('PASS');
//
const expandable = reactive({
title: ' ',
expandedRowRender: (record: any) => {},
});
// //
const columns = computed<TableColumnData[]>(() => [ const columns = computed<TableColumnData[]>(() => [
@ -273,6 +385,9 @@ const columns = computed<TableColumnData[]>(() => [
{ {
title: t('ticketTable.columns.companyName'), title: t('ticketTable.columns.companyName'),
dataIndex: 'companyName', dataIndex: 'companyName',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('ticketTable.columns.title'), title: t('ticketTable.columns.title'),
@ -281,14 +396,36 @@ const columns = computed<TableColumnData[]>(() => [
{ {
title: t('ticketTable.columns.money'), title: t('ticketTable.columns.money'),
dataIndex: 'money', dataIndex: 'money',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: t('ticketTable.columns.userName'),
dataIndex: 'userName',
sortable: {
sortDirections: ['ascend', 'descend'],
},
},
{
title: t('ticketTable.columns.createTime'),
dataIndex: 'createTime',
sortable: {
sortDirections: ['ascend', 'descend'],
},
slotName: 'createTime',
}, },
{ {
title: t('ticketTable.columns.type'), title: t('ticketTable.columns.type'),
dataIndex: 'type', dataIndex: 'type',
sortable: {
sortDirections: ['ascend', 'descend'],
},
}, },
{ {
title: t('ticketTable.columns.status'), title: t('ticketTable.columns.status'),
dataIndex: 'status', dataIndex: 'status',
slotName: 'status',
}, },
{ {
title: t('searchTable.columns.operations'), title: t('searchTable.columns.operations'),
@ -312,39 +449,56 @@ const typesOptions = computed<SelectOptionData[]>(() => [
}, },
]); ]);
const statusOptions = computed<SelectOptionData[]>(() => [
{
label: t('unsubmitted'),
value: 'SUBMIT',
},
{
label: t('unreviewed'),
value: 'EXAMINE',
},
{
label: t('pass'),
value: 'PASS',
},
{
label: t('failed'),
value: 'FAILED',
},
]);
// //
const basePagination: Pagination = { const pagination: Pagination = {
page: 1, page: 1,
size: 10, size: 10,
current: 1,
total: 1,
}; };
// //
const pagination = reactive({ const generateExcel = () => {
...basePagination, const param: DownloadExcelPrams = {
}); columns: [
{ title: '公司', key: 'companyName' },
{ title: '标题', key: 'title' },
{ title: '金额', key: 'money' },
{ title: '内容', key: 'body' },
{ title: '联系邮箱', key: 'contactEmail' },
{ title: '创建时间', key: 'createTime' },
{ title: '类型', key: 'type' },
{ title: '审核状态', key: 'status' },
],
rows: renderData.value,
name: '票据表格',
};
downloadExcel(param);
};
//
const onSelect = (dateString: any, date: any) => {
formModel.value.startTime = String(dateString[0]);
formModel.value.endTime = String(dateString[1]);
};
//
const onChange = (dateString: any, date: any) => {
formModel.value.startTime = String(dateString[0]);
formModel.value.endTime = String(dateString[1]);
};
// //
const onPageChange = (current: number) => { const onPageChange = (current: number) => {
fetchData({ ...basePagination, ...formModel.value, current }); pagination.page = current;
pagination.current = current;
search();
};
//
const onSizeChange = (size: number) => {
pagination.size = size;
search();
}; };
// //
@ -383,22 +537,32 @@ const fetchData = async (params: {
// //
const search = () => { const search = () => {
// time
const { time, ...auditDate } = formModel.value;
fetchData({ fetchData({
...basePagination, ...pagination,
...formModel.value, ...auditDate,
current: '1',
} as unknown as any); } as unknown as any);
}; };
//
const changeTop = (key: any) => {
formModel.value.status = key;
currentStatus.value = key;
search();
};
search(); search();
// //
const reset = () => { const reset = () => {
formModel.value = generateFormModel(); formModel.value = generateFormModel();
formModel.value.status = currentStatus.value;
}; };
// //
const handleDelete = async (record) => { const handleDelete = async (record: any) => {
const res = await ticketStore.removeTicket(record.id); const res = await ticketStore.removeTicket(record.id);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({

View File

@ -16,10 +16,19 @@ export default {
'searchTable.form.companyName.placeholder': 'Please enter Company', 'searchTable.form.companyName.placeholder': 'Please enter Company',
'searchTable.form.title':'Title', 'searchTable.form.title':'Title',
'searchTable.form.title.placeholder': 'Please enter Title', 'searchTable.form.title.placeholder': 'Please enter Title',
'searchTable.form.money':'Money',
'searchTable.form.money.placeholder': 'Please enter Money',
'searchTable.form.userName':'UserName',
'searchTable.form.userName.placeholder': 'Please enter UserName',
'searchTable.form.type':'Type', 'searchTable.form.type':'Type',
'searchTable.form.type.placeholder': 'Please select Type', 'searchTable.form.type.placeholder': 'Please select Type',
'searchTable.form.status': 'Status', 'searchTable.form.status': 'Status',
'searchTable.form.status.placeholder': 'Please select Status', 'searchTable.form.status.placeholder': 'Please select Status',
'searchTable.form.time':'Time',
'searchTable.form.time.placeholder': 'Please select Time',
'searchTable.form.min':'Min money',
'searchTable.form.max':'Max money',
'Confirm the deletion of this ticket': 'Confirm the deletion of this ticket?', 'Confirm the deletion of this ticket': 'Confirm the deletion of this ticket?',
@ -44,37 +53,39 @@ export default {
'unreviewed': 'Unreviewed', 'unreviewed': 'Unreviewed',
'pass': 'Pass', 'pass': 'Pass',
'failed': 'Failed', 'failed': 'Failed',
'drafts': 'Drafts',
"ticket.info.companyName": 'Company', "ticket.info.companyName": 'Company:',
"ticket.info.companyName.required": 'CompanyName is required', "ticket.info.companyName.required": 'CompanyName is required',
"ticket.info.companyName.placeholder": 'Please enter CompanyName', "ticket.info.companyName.placeholder": 'Please enter CompanyName',
"ticket.info.title": 'Title', "ticket.info.title": 'Title:',
"ticket.info.title.required": 'Title is required', "ticket.info.title.required": 'Title is required',
"ticket.info.title.placeholder": 'Please enter Title', "ticket.info.title.placeholder": 'Please enter Title',
"ticket.info.body": 'Content', "ticket.info.body": 'Content:',
"ticket.info.body.required": 'Content is required', "ticket.info.body.required": 'Content is required',
"ticket.info.body.placeholder": 'Please enter Content', "ticket.info.body.placeholder": 'Please enter Content',
"ticket.info.money": 'Money', "ticket.info.money": 'Money:',
"ticket.info.money.required": 'Money is required', "ticket.info.money.required": 'Money is required',
"ticket.info.money.placeholder": 'Please enter Money', "ticket.info.money.placeholder": 'Please enter Money',
"ticket.info.contactEmail": 'Email', "ticket.info.contactEmail": 'Email:',
"ticket.info.createTime": 'CreateTime', "ticket.info.createTime": 'CreateTime:',
"ticket.info.contactEmail.required": 'contactEmail is required', "ticket.info.contactEmail.required": 'contactEmail is required',
"ticket.info.contactEmail.placeholder": 'Please enter contactEmail', "ticket.info.contactEmail.placeholder": 'Please enter contactEmail',
"ticket.info.type": 'Type', "ticket.info.type": 'Type:',
"ticket.info.type.required": 'Type is required', "ticket.info.type.required": 'Type is required',
"ticket.info.type.placeholder": 'Please select Type', "ticket.info.type.placeholder": 'Please select Type',
"ticket.info.attachment": 'Attachment', "ticket.info.attachment": 'Attachment:',
"ticket.info.dept": 'Dept', "ticket.info.dept": 'Dept:',
"ticket.info.dept.required": 'Dept is required', "ticket.info.dept.required": 'Dept is required',
"ticket.info.dept.placeholder": 'Please select Dept', "ticket.info.dept.placeholder": 'Please select Dept',
"ticket.info.auditor": 'Auditor', "ticket.info.auditor": 'Auditor:',
"ticket.info.auditor.required": 'Auditor is required', "ticket.info.auditor.required": 'Auditor is required',
"ticket.info.auditor.placeholder": 'Please select Auditor', "ticket.info.auditor.placeholder": 'Please select Auditor',
"ticket.info.status": 'Status', "ticket.info.status": 'Status:',
"ticket.info.status.required": 'Status is required', "ticket.info.status.required": 'Status is required',
"ticket.info.status.placeholder": 'Please select Status', "ticket.info.status.placeholder": 'Please select Status',
"ticket.info.comment": 'Comment', "ticket.info.comment": 'Comment:',
"ticketTable.columns.userName":'userName',
'upload.sucess': 'Upload Sucess', 'upload.sucess': 'Upload Sucess',
'upload.fail': 'Upload Fail', 'upload.fail': 'Upload Fail',

View File

@ -15,10 +15,19 @@ export default {
'searchTable.form.companyName.placeholder': '请输入公司名称', 'searchTable.form.companyName.placeholder': '请输入公司名称',
'searchTable.form.title':'标题', 'searchTable.form.title':'标题',
'searchTable.form.title.placeholder': '请输入标题', 'searchTable.form.title.placeholder': '请输入标题',
'searchTable.form.money':'金额',
'searchTable.form.money.placeholder': '请输入金额',
'searchTable.form.userName':'用户',
'searchTable.form.committer.placeholder': '请输入票据提交的用户',
'searchTable.form.type':'类型', 'searchTable.form.type':'类型',
'searchTable.form.type.placeholder': '请选择票据类型', 'searchTable.form.type.placeholder': '请选择票据类型',
'searchTable.form.status': '状态', 'searchTable.form.status': '状态',
'searchTable.form.status.placeholder': '请选择状态', 'searchTable.form.status.placeholder': '请选择状态',
'searchTable.form.time':'时间',
'searchTable.form.time.placeholder': '请选择时间段',
'searchTable.form.min':'最小金额',
'searchTable.form.max':'最大金额',
'Confirm the deletion of this ticket': '确定删除此票据?', 'Confirm the deletion of this ticket': '确定删除此票据?',
@ -43,37 +52,40 @@ export default {
'unreviewed': '待审核', 'unreviewed': '待审核',
'pass': '审核通过', 'pass': '审核通过',
'failed': '审核未通过', 'failed': '审核未通过',
'drafts':'草稿箱',
"ticket.info.companyName": '公司', "ticket.info.companyName": '公司:',
"ticket.info.companyName.required": '公司名称不能为空', "ticket.info.companyName.required": '公司名称不能为空',
"ticket.info.companyName.placeholder": '请输入公司名称', "ticket.info.companyName.placeholder": '请输入公司名称',
"ticket.info.title": '标题', "ticket.info.title": '标题:',
"ticket.info.title.required": '标题不能为空', "ticket.info.title.required": '标题不能为空',
"ticket.info.title.placeholder": '请输入标题', "ticket.info.title.placeholder": '请输入标题',
"ticket.info.body": '内容', "ticket.info.body": '内容:',
"ticket.info.body.required": '内容不能为空', "ticket.info.body.required": '内容不能为空',
"ticket.info.body.placeholder": '请输入内容', "ticket.info.body.placeholder": '请输入内容',
"ticket.info.money": '金额', "ticket.info.money": '金额:',
"ticket.info.money.required": '金额不能为空', "ticket.info.money.required": '金额不能为空',
"ticket.info.money.placeholder": '请输入金额', "ticket.info.money.placeholder": '请输入金额',
"ticket.info.contactEmail": '联系邮箱', "ticket.info.contactEmail": '联系邮箱:',
"ticket.info.createTime": '创建时间', "ticket.info.createTime": '创建时间:',
"ticket.info.contactEmail.required": '联系邮箱不能为空', "ticket.info.contactEmail.required": '联系邮箱不能为空',
"ticket.info.contactEmail.placeholder": '请输入联系邮箱', "ticket.info.contactEmail.placeholder": '请输入联系邮箱',
"ticket.info.type": '票据类型', "ticket.info.type": '票据类型:',
"ticket.info.type.required": '票据类型不能为空', "ticket.info.type.required": '票据类型不能为空',
"ticket.info.type.placeholder": '请选择票据类型', "ticket.info.type.placeholder": '请选择票据类型',
"ticket.info.attachment": '附件', "ticket.info.attachment": '附件:',
"ticket.info.dept": '部门', "ticket.info.dept": '部门:',
"ticket.info.dept.required": '部门不能为空', "ticket.info.dept.required": '部门不能为空',
"ticket.info.dept.placeholder": '请选择提交的部门', "ticket.info.dept.placeholder": '请选择提交的部门',
"ticket.info.auditor": '审核员', "ticket.info.auditor": '审核员:',
"ticket.info.auditor.required": '审核员不能为空', "ticket.info.auditor.required": '审核员不能为空',
"ticket.info.auditor.placeholder": '请选择审核员', "ticket.info.auditor.placeholder": '请选择审核员',
"ticket.info.status": '审核状态', "ticket.info.status": '审核状态:',
"ticket.info.status.required": '审核状态不能为空', "ticket.info.status.required": '审核状态不能为空',
"ticket.info.status.placeholder": '请选择审核状态', "ticket.info.status.placeholder": '请选择审核状态',
"ticket.info.comment": '审核意见', "ticket.info.comment": '审核意见:',
"ticketTable.columns.userName":'用户',
'upload.sucess': '上传成功', 'upload.sucess': '上传成功',
'upload.fail': '上传失败', 'upload.fail': '上传失败',

View File

@ -6,7 +6,7 @@
:label-col-props="{ span: 8 }" :label-col-props="{ span: 8 }"
:wrapper-col-props="{ span: 16 }" :wrapper-col-props="{ span: 16 }"
> >
<a-form-item <!-- <a-form-item
field="oldPassword" field="oldPassword"
:label="$t('userSetting.passwordReset.form.label.oldPassword')" :label="$t('userSetting.passwordReset.form.label.oldPassword')"
:rules="[{ required: true, message: $t('login.form.password.errMsg') }]" :rules="[{ required: true, message: $t('login.form.password.errMsg') }]"
@ -19,7 +19,7 @@
allow-clear allow-clear
> >
</a-input-password> </a-input-password>
</a-form-item> </a-form-item> -->
<a-form-item <a-form-item
field="password" field="password"

View File

@ -93,7 +93,7 @@ const fileList = ref<FileItem[]>([file]);
const Onchange = async (option: any) => { const Onchange = async (option: any) => {
const FormDatas = new FormData(); const FormDatas = new FormData();
FormDatas.append('file', option.fileItem.file); FormDatas.append('file', option.fileItem.file);
const res = await ticketStore.uploadFileTicket(FormDatas); const res = await ticketStore.uploadFile(FormDatas);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
content: t('upload.sucess'), content: t('upload.sucess'),

12304
yarn.lock

File diff suppressed because it is too large Load Diff