提交票据审核及页面权限分配

This commit is contained in:
2024-02-19 10:36:33 +08:00
parent 2ba70dd160
commit a00df77cf6
29 changed files with 1226 additions and 499 deletions

View File

@ -2,6 +2,7 @@ import axios from 'axios';
import type { AxiosRequestConfig, AxiosResponse } from 'axios'; import type { AxiosRequestConfig, AxiosResponse } from 'axios';
import { Message, Modal } from '@arco-design/web-vue'; import { Message, Modal } from '@arco-design/web-vue';
import { getToken } from '@/utils/auth'; import { getToken } from '@/utils/auth';
import { useUserStore } from '@/store';
export interface HttpResponse<T = unknown> { export interface HttpResponse<T = unknown> {
status: number; status: number;

View File

@ -28,22 +28,22 @@ export function enabled(id: string) {
return axios.patch(`/api/rest/role/${id}/toggle`); return axios.patch(`/api/rest/role/${id}/toggle`);
} }
// 删除用户 // 删除
export function remove(id: string) { export function remove(id: string) {
return axios.delete(`/api/rest/role/${id}`); return axios.delete(`/api/rest/role/${id}`);
} }
// 添加角色 // 添加
export function create(data: RoleCreateRecord) { export function create(data: RoleCreateRecord) {
return axios.post(`/api/rest/role`, data); return axios.post(`/api/rest/role`, data);
} }
// 更新用户 // 更新
export function update(data: RoleRecord) { export function update(data: RoleRecord) {
return axios.patch(`/api/rest/role/${data.id}`, data); return axios.patch(`/api/rest/role/${data.id}`, data);
} }
// 获取用户详情 // 获取详情
export function getDetail(id: string) { export function getDetail(id: string) {
return axios.get<RoleRecord>(`/api/rest/role/${id}`); return axios.get<RoleRecord>(`/api/rest/role/${id}`);
} }

View File

@ -1,19 +1,28 @@
import axios from 'axios'; import axios from 'axios';
export interface TicketCreateRecord { export interface TicketCreateRecord {
remark: string;
createTime: string;
title: string; title: string;
body: string; body: string;
money: undefined; money: undefined;
status: string;
type: string; type: string;
contactEmail: string; contactEmail: string;
companyName: string; companyName: string;
attachId: string;
auditorId: string;
submit: boolean;
userId: undefined;
} }
export interface TicketRecord extends TicketCreateRecord { export interface TicketRecord extends TicketCreateRecord {
value: any;
id: undefined; id: undefined;
status: string
}
export interface auditRecord {
auditorId: string;
comment: string;
result: string;
ticketId: string;
} }
export function queryTicket(data: any) { export function queryTicket(data: any) {
@ -28,9 +37,10 @@ export function getDetail(id: string) {
return axios.get(`/api/rest/bill/${id}`); return axios.get(`/api/rest/bill/${id}`);
} }
// 更新票据 // 更新票据
export function update(data: TicketRecord) { export function update(data: TicketRecord) {
return axios.patch(`/api/rest/user/${data.id}`, data); return axios.patch(`/api/rest/bill/${data.id}`, data);
} }
// 删除票据 // 删除票据
@ -42,3 +52,28 @@ export function remove(id: string) {
export function create(params: TicketCreateRecord) { export function create(params: TicketCreateRecord) {
return axios.post('/api/rest/bill', params); return axios.post('/api/rest/bill', params);
} }
// 上传附件
export function uploadFile(file: any) {
return axios.post('/api/rest/attachment', file);
}
// 获取附件信息
export function attachment(id:string){
return axios.get(`/api/rest/attachment/find/${id}`)
}
// 审核
export function audit(ticketId: string, params: auditRecord) {
return axios.patch(`/api/rest/bill/audit/${ticketId}`, params);
}
// 审核员管理的票据列表
export function auditTickctList(data: any){
return axios({
url: '/api/rest/bill/audit/list', // 路径
method: 'get',
params: data, // 参数
});
}

View File

@ -31,6 +31,7 @@ export interface PasswordReSetModel {
// 添加用户数据 // 添加用户数据
export interface CreateRecord { export interface CreateRecord {
username: string; username: string;
nickName: string; nickName: string;
password: string; password: string;
@ -39,8 +40,9 @@ export interface CreateRecord {
enabled: string; enabled: string;
address: string; address: string;
deptId: DeptRecord | undefined; deptId: DeptRecord | undefined;
roleId: RoleRecord | undefined; roleId: string| RoleRecord | undefined;
permissionIds: (number | undefined)[]; permissionIds: (number | undefined)[];
authorities: string[];
} }
export interface SelfRecord { export interface SelfRecord {
@ -53,12 +55,16 @@ export interface SelfRecord {
// 用户数据 // 用户数据
export interface UserRecord extends CreateRecord { export interface UserRecord extends CreateRecord {
id: number; value: any;
id: string;
avatar: string;
createAt: string
} }
export interface UserParams extends Partial<UserRecord> { export interface UserParams extends Partial<UserRecord> {
page: number; page: number;
size: number; size: number;
current: number
} }
export interface Pageable { export interface Pageable {
@ -108,14 +114,20 @@ export function create(data: CreateRecord) {
} }
// 模糊查询用户列表 // 模糊查询用户列表
export function queryUserList(params: UserParams) { export function queryUserList(params: any) {
return axios({ return axios({
url: '/api/rest/user/query', url: '/api/rest/user',
params, params,
method: 'get', method: 'get',
}); });
} }
// 根据id查询用户信息
export function userDetail(id: string){
return axios.get(`/api/rest/user/${id}`)
}
// 是否启用 // 是否启用
export function enabled(id: string) { export function enabled(id: string) {
return axios.patch(`/api/rest/user/${id}/toggle`); return axios.patch(`/api/rest/user/${id}/toggle`);
@ -135,15 +147,23 @@ export function selfUpdate(data: UserState) {
return axios.patch<Res>(`/api/rest/user/self`, data); return axios.patch<Res>(`/api/rest/user/self`, data);
} }
export function switchRole(roleId: number) {
return axios.patch<UserState>(`/api/user/self/switch-role/${roleId}`);
}
// 获取个人用户信息 // 获取个人用户信息
export function getUserInfo() { export function getUserInfo() {
return axios.get<UserState>('/api/rest/user/self'); return axios.get<UserState>('/api/rest/user/self');
} }
// 部门的审核员
export function deptAudit(id: string,roleId:string){
return axios({
url: `/api/rest/user/dept/${id}?roleId=${roleId}`, // 路径
method: 'get',
});
}
export function switchRole(roleId: number) {
return axios.patch<UserState>(`/api/user/self/switch-role/${roleId}`);
}
export function getUserDetail(id: number) { export function getUserDetail(id: number) {
return axios.get<UserState>(`/api/user/${id}`); return axios.get<UserState>(`/api/user/${id}`);
} }

View File

@ -204,8 +204,11 @@ import useUser from '@/hooks/user';
import Menu from '@/components/menu/index.vue'; import Menu from '@/components/menu/index.vue';
import userIcon from '@/assets/images/user-circle.png'; import userIcon from '@/assets/images/user-circle.png';
import { SelectOptionData } from '@arco-design/web-vue/es/select/interface'; import { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import { useRouter } from 'vue-router';
import { clearToken } from '@/utils/auth';
import MessageBox from '../message-box/index.vue'; import MessageBox from '../message-box/index.vue';
const router = useRouter();
const appStore = useAppStore(); const appStore = useAppStore();
const userStore = useUserStore(); const userStore = useUserStore();
const { logout } = useUser(); const { logout } = useUser();
@ -257,7 +260,12 @@ const setPopoverVisible = () => {
refBtn.value.dispatchEvent(event); refBtn.value.dispatchEvent(event);
}; };
const handleLogout = () => { const handleLogout = () => {
logout(); // logout();
clearToken();
router.push({
name: 'login',
});
}; };
const setDropDownVisible = () => { const setDropDownVisible = () => {
const event = new MouseEvent('click', { const event = new MouseEvent('click', {

View File

@ -6,12 +6,13 @@ export default function usePermission() {
const userStore = useUserStore(); const userStore = useUserStore();
return { return {
accessRouter(route: RouteLocationNormalized | RouteRecordRaw) { accessRouter(route: RouteLocationNormalized | RouteRecordRaw) {
return ( return (
!route.meta?.requiresAuth || !route.meta?.requiresAuth ||
!route.meta?.permissions || !route.meta?.permissions ||
route.meta?.permissions?.includes('*') || route.meta?.permissions?.includes('*') ||
intersection(route.meta?.permissions, userStore.permissions).length > 0 intersection(route.meta?.permissions, userStore.permissions).length > 0
// route.meta?.permissions?.includes(userStore.permissions) || route.meta?.permissions?.includes(userStore.permissions)
); );
}, },
// TODO 不知道是干嘛的 // TODO 不知道是干嘛的

View File

@ -12,6 +12,7 @@ export default function setupPermissionGuard(router: Router) {
const userStore = useUserStore(); const userStore = useUserStore();
const Permission = usePermission(); const Permission = usePermission();
const permissionsAllow = Permission.accessRouter(to); const permissionsAllow = Permission.accessRouter(to);
if (appStore.menuFromServer) { if (appStore.menuFromServer) {
// 针对来自服务端的菜单配置进行处理 // 针对来自服务端的菜单配置进行处理
// Handle routing configuration from the server // Handle routing configuration from the server

View File

@ -14,6 +14,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
} else { } else {
try { try {
await userStore.info(); await userStore.info();
next(); next();
} catch (error) { } catch (error) {
await userStore.logout(); await userStore.logout();

View File

@ -1,10 +1,10 @@
export default { // export default {
path: 'https://arco.design', // path: 'https://arco.design',
name: 'arcoWebsite', // name: 'arcoWebsite',
meta: { // meta: {
locale: 'menu.arcoWebsite', // locale: 'menu.arcoWebsite',
icon: 'icon-link', // icon: 'icon-link',
requiresAuth: true, // requiresAuth: true,
order: 8, // order: 8,
}, // },
}; // };

View File

@ -1,10 +1,10 @@
export default { // export default {
path: 'https://arco.design/vue/docs/pro/faq', // path: 'https://arco.design/vue/docs/pro/faq',
name: 'faq', // name: 'faq',
meta: { // meta: {
locale: 'menu.faq', // locale: 'menu.faq',
icon: 'icon-question-circle', // icon: 'icon-question-circle',
requiresAuth: true, // requiresAuth: true,
order: 9, // order: 9,
}, // },
}; // };

View File

@ -23,16 +23,16 @@ const DASHBOARD: AppRouteRecordRaw = {
}, },
}, },
{ // {
path: 'monitor', // 一级路径 // path: 'monitor', // 一级路径
name: 'Monitor', // 路由名称 // name: 'Monitor', // 路由名称
component: () => import('@/views/dashboard/monitor/index.vue'), // 要跳转的视图,这里要跳转到页面的基本布局 // component: () => import('@/views/dashboard/monitor/index.vue'), // 要跳转的视图,这里要跳转到页面的基本布局
meta: { // meta: {
locale: 'menu.dashboard.monitor', // 菜单名字 // locale: 'menu.dashboard.monitor', // 菜单名字
requiresAuth: true, // 需要登录鉴权 // requiresAuth: true, // 需要登录鉴权
permissions: ['admin'], // 只允许管理员用户访问 // permissions: ['admin'], // 只允许管理员用户访问
}, // },
}, // },
], ],
}; };

View File

@ -29,7 +29,7 @@ const SYSTEM: AppRouteRecordRaw = {
meta: { meta: {
locale: 'menu.system.role', locale: 'menu.system.role',
requiresAuth: true, requiresAuth: true,
permissions: ['*'], permissions: ['admin'],
}, },
}, },
{ {
@ -39,7 +39,7 @@ const SYSTEM: AppRouteRecordRaw = {
meta: { meta: {
locale: 'menu.system.dept', locale: 'menu.system.dept',
requiresAuth: true, requiresAuth: true,
permissions: ['*'], permissions: ['admin'],
}, },
}, },
{ {
@ -49,7 +49,7 @@ const SYSTEM: AppRouteRecordRaw = {
meta: { meta: {
locale: 'menu.system.user', locale: 'menu.system.user',
requiresAuth: true, requiresAuth: true,
permissions: ['*'], permissions: ['admin'],
}, },
}, },
], ],

View File

@ -22,16 +22,16 @@ const TICKET: AppRouteRecordRaw = {
permissions: ['*'], permissions: ['*'],
}, },
}, },
{ // {
path: 'form', // path: 'form',
name: 'TicketForm', // name: 'TicketForm',
component: () => import('@/views/ticket/form/index.vue'), // component: () => import('@/views/ticket/form/index.vue'),
meta: { // meta: {
locale: 'menu.ticket.form', // locale: 'menu.ticket.form',
requiresAuth: true, // requiresAuth: true,
permissions: ['*'], // permissions: ['*'],
}, // },
}, // },
], ],
}; };

View File

@ -2,11 +2,17 @@ import { defineStore } from 'pinia';
import { import {
TicketRecord, TicketRecord,
TicketCreateRecord, TicketCreateRecord,
auditRecord,
queryTicket, queryTicket,
remove, remove,
getDetail, getDetail,
create, create,
update, update,
uploadFile,
attachment,
audit,
auditTickctList,
} from '@/api/ticket'; } from '@/api/ticket';
import { ticketStore } from './type'; import { ticketStore } from './type';
@ -22,6 +28,7 @@ const useTicketStore = defineStore('ticket', {
type: undefined, type: undefined,
contactEmail: undefined, contactEmail: undefined,
companyName: undefined, companyName: undefined,
userId: undefined,
}), }),
getters: { getters: {
@ -49,6 +56,22 @@ const useTicketStore = defineStore('ticket', {
async updateTicket(data: TicketRecord) { async updateTicket(data: TicketRecord) {
return update(data); return update(data);
}, },
async uploadFileTicket(file: any) {
return uploadFile(file);
},
async getAttachment(id: string){
return attachment(id);
},
async auditTicket(ticketId: string, data: auditRecord) {
return audit(ticketId, data);
},
async auditTickctList(data:any){
return auditTickctList(data);
}
}, },
}); });

View File

@ -9,4 +9,5 @@ export interface ticketStore {
type?: undefined; type?: undefined;
contactEmail?: string; contactEmail?: string;
companyName?: string; companyName?: string;
userId?: undefined;
} }

View File

@ -12,6 +12,8 @@ import {
create, create,
UserRecord, UserRecord,
update, update,
userDetail,
deptAudit
} 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';
@ -32,6 +34,7 @@ const useUserStore = defineStore('user', {
role: undefined, role: undefined,
roles: undefined, roles: undefined,
permissions: [], permissions: [],
authorities: [],
}), }),
getters: { getters: {
@ -51,10 +54,11 @@ const useUserStore = defineStore('user', {
this.$reset(); this.$reset();
}, },
// Get user's information
async info() { async info() {
const res = await getUserInfo(); const res = await getUserInfo();
this.setInfo(res.data); console.log("we",res);
res.data.user.permissions = res.data.permissions
this.setInfo(res.data.user);
}, },
// Get user's crsf // Get user's crsf
@ -74,7 +78,7 @@ const useUserStore = defineStore('user', {
}, },
// Get user's List // Get user's List
async getUserList(data: UserParams) { async getUserList(data: any) {
return queryUserList(data); return queryUserList(data);
}, },
@ -98,6 +102,14 @@ const useUserStore = defineStore('user', {
return update(data); return update(data);
}, },
async getUserDetail(id: string){
return userDetail(id);
},
async getDeptAudit(deptId: string,roleId: string){
return deptAudit(deptId,roleId)
},
logoutCallBack() { logoutCallBack() {
const appStore = useAppStore(); const appStore = useAppStore();
this.resetInfo(); this.resetInfo();

View File

@ -2,6 +2,7 @@ import { RoleRecord } from '@/api/role';
export type RoleType = '' | '*' | 'admin' | 'user' | string[]; export type RoleType = '' | '*' | 'admin' | 'user' | string[];
export interface UserState { export interface UserState {
[x: string]: { value: any; id: string; avatar: string; createAt: string; username: string; nickName: string; password: string; phone: string; email: string; enabled: string; address: string; deptId: { id: string; remark: string; createTime: string; name: string; enabled: string; }|undefined; roleId: string|{ id: string; name: string; dataScope: string; permissionIds: (number|undefined)[]; remark: string; authorities: (number|undefined)[]; }|undefined; permissionIds: (number|undefined)[]; authorities: string[]; };
username?: string; username?: string;
nickName?: string; nickName?: string;
avatar?: string; avatar?: string;
@ -13,5 +14,6 @@ export interface UserState {
id?: number; id?: number;
role?: RoleRecord; role?: RoleRecord;
roles?: RoleRecord[]; roles?: RoleRecord[];
permissions?: string[] | '' | '*' | 'admin' | 'user'; permissions?: string[] | '' | '*' | 'admin' | 'user'|'auditor';
authorities?: string[]
} }

View File

@ -51,21 +51,100 @@
> >
{{ $t('login.form.rememberPassword') }} {{ $t('login.form.rememberPassword') }}
</a-checkbox> </a-checkbox>
<a-link>{{ $t('login.form.forgetPassword') }}</a-link> <!-- <a-link>{{ $t('login.form.forgetPassword') }}</a-link> -->
</div> </div>
<a-button type="primary" html-type="submit" long :loading="loading"> <a-button type="primary" html-type="submit" long :loading="loading">
{{ $t('login.form.login') }} {{ $t('login.form.login') }}
</a-button> </a-button>
<a-button type="text" long class="login-form-register-btn"> <a-button
type="text"
long
class="login-form-register-btn"
@click="handleClick"
>
{{ $t('login.form.register') }} {{ $t('login.form.register') }}
</a-button> </a-button>
</a-space> </a-space>
</a-form> </a-form>
</div> </div>
<a-modal
width="600px"
:visible="visible"
@ok="handleOk"
@cancel="handleCancel"
>
<template #title>{{ modalTitle }}</template>
<a-form ref="userCreateRef" :model="formData" :style="{ width: '500px' }">
<a-form-item
field="username"
label="用户名"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '用户名不能为空' }]"
>
<a-input v-model="formData.username" />
</a-form-item>
<a-form-item
field="password"
label="密码"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '密码不能为空' }]"
>
<a-input v-model="formData.password" />
</a-form-item>
<a-form-item field="nickName" label="昵称">
<a-input v-model="formData.nickName" />
</a-form-item>
<a-form-item
field="phone"
label="电话"
:rules="[
{ required: true, message: '电话不能为空' },
{ match: /^1[3-9]\d{9}$/, message: '请输入正确格式的电话号码' },
]"
:validate-trigger="['change', 'input']"
>
<a-input v-model="formData.phone" />
</a-form-item>
<a-form-item
field="email"
label="邮箱"
:rules="[
{ required: true, type: 'email', message: '请输入正确格式的邮箱' },
]"
:validate-trigger="['change', 'input']"
>
<a-input v-model="formData.email" />
</a-form-item>
<a-form-item field="address" label="地址">
<a-input v-model="formData.address" />
</a-form-item>
<a-form-item
field="deptId"
label="部门"
:rules="[{ required: true, message: '请选择部门' }]"
:validate-trigger="['change']"
>
<a-tree-select
v-model="formData.deptId"
:field-names="{
key: 'id',
title: 'name',
children: 'children',
}"
:data="deptOptions"
allow-clear
placeholder="请选择父部门 ..."
/>
</a-form-item>
</a-form>
</a-modal>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import useVisible from '@/hooks/visible';
import { FormInstance } from '@arco-design/web-vue/es/form';
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
@ -74,14 +153,35 @@ import { useI18n } from 'vue-i18n';
import { useStorage } from '@vueuse/core'; import { useStorage } from '@vueuse/core';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import useLoading from '@/hooks/loading'; import useLoading from '@/hooks/loading';
import { LoginData } from '@/api/user'; import { LoginData, CreateRecord } from '@/api/user';
import { deptList } from '@/api/dept';
import { computedAsync } from '@vueuse/core';
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const errorMessage = ref(''); const errorMessage = ref('');
const { loading, setLoading } = useLoading(); const { loading, setLoading } = useLoading();
const userStore = useUserStore(); const userStore = useUserStore();
const { visible, setVisible } = useVisible(false);
const userCreateRef = ref<FormInstance>();
const modalTitle = '注册用户信息';
const formData = ref<CreateRecord>({
username: '',
nickName: '',
password: '',
phone: '',
email: '',
enabled: 'true',
address: '',
deptId: undefined,
roleId: undefined,
permissionIds: [],
authorities: [],
});
const deptOptions = computedAsync(async () => {
const { data } = await deptList();
return data;
});
const loginConfig = useStorage('login-config', { const loginConfig = useStorage('login-config', {
// 使 useStorage // 使 useStorage
rememberPassword: true, rememberPassword: true,
@ -114,7 +214,7 @@ const handleSubmit = async ({
const res = await userStore.me(); const res = await userStore.me();
await userStore.login(values as LoginData, res.data.csrf.token); await userStore.login(values as LoginData, res.data.csrf.token);
const csrf = await userStore.me(); const csrf = await userStore.me();
await userStore.info();
// //
const { redirect, ...othersQuery } = router.currentRoute.value.query; const { redirect, ...othersQuery } = router.currentRoute.value.query;
router.push({ router.push({
@ -144,6 +244,30 @@ const handleSubmit = async ({
const setRememberPassword = (value: boolean) => { const setRememberPassword = (value: boolean) => {
loginConfig.value.rememberPassword = value; loginConfig.value.rememberPassword = value;
}; };
const handleClick = () => {
setVisible(true);
};
const handleCancel = async () => {
userCreateRef.value?.resetFields();
setVisible(false);
};
const handleOk = async () => {
const valid = await userCreateRef.value?.validate();
if (!valid) {
const res = await userStore.createUser(formData.value);
if (res.status === 200) {
Message.success({
content: `${modalTitle}成功`,
duration: 5 * 1000,
});
}
userCreateRef.value?.resetFields();
setVisible(false);
}
};
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>

View File

@ -132,7 +132,7 @@
:model-value="record.enabled" :model-value="record.enabled"
:checked-value="true" :checked-value="true"
:unchecked-value="false" :unchecked-value="false"
@change="enabledStatus(record.id)" @change="enabledStatus(record)"
/> />
</template> </template>
<template #operations="{ record }"> <template #operations="{ record }">
@ -254,8 +254,9 @@ const reset = () => {
}; };
// //
const enabledStatus = async (id: string) => { const enabledStatus = async (record: string) => {
const res = await deptStore.enabledDept(id); record.enabled = !record.enabled;
const res = await deptStore.enabledDept(record.id);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
content: t('modify.status.sucess'), content: t('modify.status.sucess'),
@ -267,7 +268,7 @@ const enabledStatus = async (id: string) => {
duration: 3 * 1000, duration: 3 * 1000,
}); });
} }
search(); // search();
}; };
const handleDelete = async (record) => { const handleDelete = async (record) => {

View File

@ -193,6 +193,7 @@ fetchData();
// //
const enabledStatus = async (record: string) => { const enabledStatus = async (record: string) => {
record.enabled = !record.enabled;
const res = await roleStore.enabledRole(record.id); const res = await roleStore.enabledRole(record.id);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
@ -205,7 +206,7 @@ const enabledStatus = async (record: string) => {
duration: 3 * 1000, duration: 3 * 1000,
}); });
} }
fetchData(); // fetchData();
}; };
const handleDelete = async (record: RoleRecord) => { const handleDelete = async (record: RoleRecord) => {

View File

@ -20,7 +20,7 @@
@cancel="handleCancel" @cancel="handleCancel"
> >
<template #title>{{ modalTitle }}</template> <template #title>{{ modalTitle }}</template>
<a-form ref="userCreateRef" :model="formData" :style="{ width: '500px' }"> <a-form ref="CreateRef" :model="formData" :style="{ width: '500px' }">
<a-form-item <a-form-item
field="username" field="username"
label="用户名" label="用户名"
@ -140,6 +140,7 @@ const formData = ref<CreateRecord>({
deptId: undefined, deptId: undefined,
roleId: undefined, roleId: undefined,
permissionIds: [], permissionIds: [],
authorities: [],
}); });
let formDifer = {}; let formDifer = {};
@ -197,7 +198,6 @@ const handleSubmit = async () => {
}); });
} else { } else {
formDifer.id = formData.value.id; formDifer.id = formData.value.id;
console.log('2', formDifer);
const res = await userStore.updateUser(formDifer); const res = await userStore.updateUser(formDifer);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
@ -207,14 +207,15 @@ const handleSubmit = async () => {
} }
} }
} }
emit('refresh'); // emit('refresh');
checkKeys.value = []; checkKeys.value = [];
setVisible(false); setVisible(false);
} }
}; };
const handleCancel = async () => { const handleCancel = async () => {
userCreateRef.value?.resetFields(); /// emit('refresh');
// userCreateRef.value?.resetFields();
checkKeys.value = []; checkKeys.value = [];
setVisible(false); setVisible(false);
}; };

View File

@ -47,11 +47,11 @@
</a-col> </a-col>
<a-col :span="9"> <a-col :span="9">
<a-form-item <a-form-item
field="enabled" field="enable"
:label="$t('searchTable.form.status')" :label="$t('searchTable.form.status')"
> >
<a-select <a-select
v-model="formModel.enabled" v-model="formModel.enable"
:options="statusOptions" :options="statusOptions"
:placeholder="$t('searchTable.form.status.placeholder')" :placeholder="$t('searchTable.form.status.placeholder')"
/> />
@ -223,7 +223,7 @@ const generateFormModel = () => {
phone: '', phone: '',
email: '', email: '',
createdTime: [], createdTime: [],
enabled: '', enable: '',
}; };
}; };
@ -302,14 +302,15 @@ const statusOptions = computed<SelectOptionData[]>(() => [
value: 'false', value: 'false',
}, },
]); ]);
const fetchData = async (params: UserParams = { page: 1, size: 10 }) => { const fetchData = async (
params: UserParams = { page: 1, size: 10, current: 1 }
) => {
setLoading(true); setLoading(true);
try { try {
const res = await userStore.getUserList(params); const res = await userStore.getUserList(params);
const { data } = res; renderData.value = res.data.records;
renderData.value = data.list; pagination.current = res.data.current;
pagination.current = params.page; pagination.total = res.data.total;
pagination.total = data.total;
// pagination.size = data.size; // pagination.size = data.size;
} catch (err) { } catch (err) {
// you can report use errorHandler or other // you can report use errorHandler or other
@ -327,7 +328,7 @@ const search = () => {
const onPageChange = (current: number) => { const onPageChange = (current: number) => {
const page = current; const page = current;
fetchData({ ...basePagination, page }); fetchData({ ...basePagination, current });
}; };
fetchData(); fetchData();
@ -337,6 +338,7 @@ const reset = () => {
// //
const enabledStatus = async (record: string) => { const enabledStatus = async (record: string) => {
record.enabled = !record.enabled;
const res = await userStore.enabledUser(record.id); const res = await userStore.enabledUser(record.id);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
@ -349,7 +351,6 @@ const enabledStatus = async (record: string) => {
duration: 3 * 1000, duration: 3 * 1000,
}); });
} }
search();
}; };
const handleDelete = async (record: UserRecord) => { const handleDelete = async (record: UserRecord) => {

View File

@ -1,6 +1,15 @@
<template> <template>
<a-space width="450px" v-if="!props.isCreate"> <a-space width="450px">
<a-form ref="createEditRef" :model="formData" :style="{ width: '450px' }">
<a-form
v-if="visible"
ref="createEditRef"
:model="formData"
:style="{ width: '450px' }"
>
<a-form style="text-align: center" v-model="formTitle">
<h2>{{ formTitle }}</h2>
</a-form>
<a-form-item <a-form-item
field="companyName" field="companyName"
label="公司" label="公司"
@ -37,10 +46,6 @@
<a-input v-model="formData.money" /> <a-input v-model="formData.money" />
</a-form-item> </a-form-item>
<a-form-item field="createTime" label="创建时间">
<a-input v-model="formData.createTime" />
</a-form-item>
<a-form-item <a-form-item
field="contactEmail" field="contactEmail"
label="联系邮箱" label="联系邮箱"
@ -56,41 +61,73 @@
:validate-trigger="['change', 'input']" :validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '票据类型不能为空' }]" :rules="[{ required: true, message: '票据类型不能为空' }]"
> >
<a-input v-model="formData.type" /> <a-select
v-model="formData.type"
:options="typesOptions"
:placeholder="$t('searchTable.form.type.placeholder')"
/>
</a-form-item> </a-form-item>
<a-form-item field="status" label="审核状态" v-if="props.isDetail"> <a-form-item field="attachment" label="附件">
<div> <a-upload
{{ formData.status }} :file-list="fileList"
</div> :custom-request="Onchange"
</a-form-item> :limit="1"
<a-form-item :on-before-remove="removeAttact"
v-else v-model="formData.attachId"
field="status"
label="审核状态"
:disabled="props.isDetail"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '审核状态不能为空' }]"
>
<a-select
v-model="formData.status"
:options="statusOptions"
:placeholder="$t('searchTable.form.status.placeholder')"
/> />
</a-form-item> </a-form-item>
<a-form-item
field="deptId"
label="部门"
:rules="[{ required: true, message: '请选择部门' }]"
:validate-trigger="['change']"
>
<a-tree-select
v-model="formData.deptId"
:field-names="{
key: 'id',
title: 'name',
children: 'children',
}"
:data="deptOptions"
allow-clear
placeholder="请选择部门 ..."
@change="optionDept()"
/>
</a-form-item>
<a-form-item field="auditorId" label="审核员">
<a-select v-model="formData.auditorId" placeholder="请选择审核员">
<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-button type="outline" @click="handleStorage">暂存</a-button>
<a-button type="primary" style="margin-left: 15px" @click="handleOk"
>提交</a-button
>
</a-form-item>
</a-form> </a-form>
</a-space> </a-space>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import { computed, PropType, ref, watch } from 'vue'; import { computed, PropType, ref, watch } from 'vue';
import { FormInstance } from '@arco-design/web-vue/es/form'; import { FormInstance } from '@arco-design/web-vue/es/form';
import { computedAsync } from '@vueuse/core';
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';
import useTableOption from '@/hooks/table-option'; import useTableOption from '@/hooks/table-option';
import { useTicketStore } from '@/store'; import { useTicketStore, useUserStore } from '@/store';
import { TicketRecord } from '@/api/ticket'; import { TicketRecord } from '@/api/ticket';
import { deptList } from '@/api/dept';
const props = defineProps({ const props = defineProps({
prem: { prem: {
@ -100,27 +137,47 @@ const props = defineProps({
isVisible: Boolean, isVisible: Boolean,
}); });
console.log('ef', props); console.log('ef', props);
const { t } = useI18n();
const ticketStore = useTicketStore(); const ticketStore = useTicketStore();
const modalTitle = computed(() => { const userStore = useUserStore();
return props.isCreate ? '详情' : '审核'; const formTitle = computed(() => {
return props.isCreate ? '添加票据' : '修改票据信息';
}); });
const { visible, setVisible } = useVisible(true);
const createEditRef = ref<FormInstance>(); const createEditRef = ref<FormInstance>();
const formData = ref<TicketRecord>({ const formData = ref<TicketRecord>({
id: undefined, id: undefined,
remark: '',
createTime: '',
title: '', title: '',
body: '', body: '',
money: undefined, money: undefined,
status: '',
type: '', type: '',
contactEmail: '', contactEmail: '',
companyName: '', companyName: '',
attachId: '',
auditorId: '',
submit: '',
}); });
const emit = defineEmits(['refresh']); const emit = defineEmits(['refresh']);
const deptOptions = computedAsync(async () => {
const { data } = await deptList();
return data;
});
const typesOptions = computed<SelectOptionData[]>(() => [
{
label: '银行支票',
value: 'BANK',
},
{
label: '税务支票',
value: 'TAX',
},
{
label: '其他支票',
value: 'OTHER',
},
]);
const statusOptions = computed<SelectOptionData[]>(() => [ const statusOptions = computed<SelectOptionData[]>(() => [
{ {
label: '待审核', label: '待审核',
@ -135,43 +192,97 @@ const statusOptions = computed<SelectOptionData[]>(() => [
value: 'FAILED', value: 'FAILED',
}, },
]); ]);
const auditorOptions = ref([]);
const handleClick = () => { const optionDept = async () => {
auditorOptions.value = [];
const res = await userStore.getUserList({
const ticketId = props.prem?.id; page: 1,
ticketStore size: 100,
.getDetail(ticketId) roleId: '54',
.then((res) => { deptId: formData.value.deptId,
formData.value = res.data.bill;
})
.then(() => {
setVisible(true);
}); });
auditorOptions.value = res.data.list;
}; };
const handleSubmit = async () => { const fileList = ref([]);
const Onchange = async (option: any) => {
//
const FormDatas = new FormData();
FormDatas.append('file', option.fileItem.file);
const res = await ticketStore.uploadFileTicket(FormDatas);
console.log('res', res);
if (res.status === 200) {
Message.success({
content: t('上传成功'),
duration: 3 * 1000,
});
res.data.name = res.data.fileName;
fileList.value.push(res.data);
formData.value.attachId = res.data.id;
} else {
Message.error({
content: t('上传失败'),
duration: 3 * 1000,
});
}
};
const removeAttact = () => {
fileList.value.pop();
};
const handleStorage = async () => {
const valid = await createEditRef.value?.validate(); const valid = await createEditRef.value?.validate();
if (!valid) { if (!valid) {
formData.value.submit = false;
if (props.isCreate) { if (props.isCreate) {
createEditRef.value?.resetFields(); const res = await ticketStore.createTicket(formData.value);
if (res.status === 200) {
Message.success({
content: '暂存成功',
duration: 5 * 1000,
});
}
} else { } else {
const res = await ticketStore.updateTicket(formData.value); const res = await ticketStore.updateTicket(formData.value);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
content: `${modalTitle.value}成功`, content: '暂存成功',
duration: 5 * 1000, duration: 5 * 1000,
}); });
} }
} }
emit('refresh'); createEditRef.value?.resetFields();
setVisible(false); removeAttact();
} }
}; };
const handleCancel = async () => { const handleOk = async () => {
const valid = await createEditRef.value?.validate();
if (!valid) {
formData.value.submit = true;
if (props.isCreate) {
const res = await ticketStore.createTicket(formData.value);
if (res.status === 200) {
Message.success({
content: `提交成功`,
duration: 5 * 1000,
});
}
} else {
const res = await ticketStore.updateTicket(formData.value);
if (res.status === 200) {
Message.success({
content: `提交成功`,
duration: 5 * 1000,
});
}
}
createEditRef.value?.resetFields(); createEditRef.value?.resetFields();
setVisible(false); removeAttact();
}
}; };
</script> </script>

View File

@ -16,15 +16,17 @@
type="outline" type="outline"
size="small" size="small"
@click="handleCreate" @click="handleCreate"
style="margin-left: 70px" style="margin-left: 120px"
>新建</a-button >新建</a-button
> >
<a-list <a-list
:style="{ width: `300px` }" :style="{ width: `350px` }"
:virtualListProps="{ :virtualListProps="{
height: 550, height: 550,
}" }"
:data="uncommitData" :data="uncommitData"
:pagination-props="paginationProps"
@page-change="onPageChange"
> >
<template #item="{ item, id }"> <template #item="{ item, id }">
<a-list-item :key="id"> <a-list-item :key="id">
@ -39,89 +41,12 @@
</a-col> </a-col>
<a-divider style="height: 600px" direction="vertical" /> <a-divider style="height: 600px" direction="vertical" />
<a-col :flex="3"> <a-col :flex="3">
<!-- <TicketForm <TicketForm
ref="createEditRef" ref="createEditRef"
:prem="ticketItem" :prem="ticketItem"
:is-create="false" :is-create="true"
:is-visible="true" :is-visible="visible"
/> -->
<a-form
v-if="visible"
ref="createEditRef"
:model="formData"
:style="{ width: '450px' }"
>
<a-form style="text-align: center" v-model="formTitle">
<h2>{{ formTitle }}</h2>
</a-form>
<a-form-item
field="companyName"
label="公司"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '公司不能为空' }]"
>
<a-input v-model="formData.companyName" />
</a-form-item>
<a-form-item
field="title"
label="标题"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '标题不能为空' }]"
>
<a-input v-model="formData.title" />
</a-form-item>
<a-form-item
field="body"
label="内容"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '内容不能为空' }]"
>
<a-textarea v-model="formData.body" />
</a-form-item>
<a-form-item
field="money"
label="金额"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '金额不能为空' }]"
>
<a-input v-model="formData.money" />
</a-form-item>
<a-form-item
field="contactEmail"
label="联系邮箱"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '联系邮箱不能为空' }]"
>
<a-input v-model="formData.contactEmail" />
</a-form-item>
<a-form-item
field="type"
label="票据类型"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '票据类型不能为空' }]"
>
<a-select
v-model="formData.type"
:options="typesOptions"
:placeholder="$t('searchTable.form.type.placeholder')"
/> />
</a-form-item>
<a-form-item>
<a-button type="outline" @click="handleStorage">暂存</a-button>
<a-button
type="primary"
style="margin-left: 15px"
@click="handleOk"
>提交</a-button
>
</a-form-item>
</a-form>
</a-col> </a-col>
</a-row> </a-row>
</a-card> </a-card>
@ -133,83 +58,67 @@ import { computed, ref, watch, reactive } 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 { Pagination } from '@/types/global';
import { computedAsync } from '@vueuse/core';
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 useTableOption from '@/hooks/table-option';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { Message } from '@arco-design/web-vue'; import { FileItem, Message } from '@arco-design/web-vue';
import { FormInstance } from '@arco-design/web-vue/es/form'; import { FormInstance } from '@arco-design/web-vue/es/form';
import useVisible from '@/hooks/visible'; import useVisible from '@/hooks/visible';
import { useTicketStore } from '@/store'; import { useTicketStore, useUserStore, useDeptStore } from '@/store';
import { TicketRecord } from '@/api/ticket'; import { TicketRecord } from '@/api/ticket';
// import TicketForm from './components/form-edit.vue'; import TicketForm from './components/form-edit.vue';
const { deepClone } = useTableOption();
const createEditRef = ref<FormInstance>(); const createEditRef = ref<FormInstance>();
const { loading, setLoading } = useLoading(true); const { loading, setLoading } = useLoading(true);
const ticketStore = useTicketStore(); const ticketStore = useTicketStore();
const userStore = useUserStore();
const deptStore = useDeptStore();
const { t } = useI18n(); const { t } = useI18n();
const uncommitData = ref<TicketRecord[]>([]); const uncommitData = ref<TicketRecord[]>([]);
const ticketStatus = ref('SUBMIT'); const ticketStatus = ref('SUBMIT');
const renderData = () => { const renderData = () => {
return { return {
id: undefined, id: undefined,
remark: '',
createTime: '',
title: '', title: '',
body: '', body: '',
money: undefined, money: undefined,
status: '',
type: '', type: '',
contactEmail: '', contactEmail: '',
companyName: '', companyName: '',
attachId: '',
auditorId: '',
submit: Boolean,
}; };
}; };
const formData = ref(renderData()); const formData = ref(renderData());
const { visible, setVisible } = useVisible(false); const { visible, setVisible } = useVisible(false);
let formTitle = '新建票据'; let formTitle = '新建票据';
const isCreate = ref('false'); const isCreate = ref('true');
const typesOptions = computed<SelectOptionData[]>(() => [ const paginationProps = reactive({
{ defaultPageSize: 10,
label: 'Bank', total: 10,
value: 'Bank', });
},
{ let ticketItem = '';
label: 'Tax', const params = {
value: 'Tax', page: 1,
}, current: 1,
{ size: 10,
label: 'Other', status: ticketStatus.value,
value: 'Other', };
},
]);
const statusOptions = computed<SelectOptionData[]>(() => [
{
label: '待审核',
value: 'EXAMINE',
},
{
label: '审核通过',
value: 'PASS',
},
{
label: '审核未通过',
value: 'FAILED',
},
]);
// //
const fetchData = async () => { const fetchData = async () => {
setLoading(true); setLoading(true);
const params = {
page: 1,
current: 1,
size: 20,
status: ticketStatus.value,
};
try { try {
ticketStore.getTicketList(params).then((res) => { ticketStore.getTicketList(params).then((res) => {
uncommitData.value = res.data.records; uncommitData.value = res.data.records;
paginationProps.defaultPageSize = params.size;
paginationProps.total = res.data.total;
}); });
} catch (err) { } catch (err) {
// you can report use errorHandler or other // you can report use errorHandler or other
@ -218,6 +127,12 @@ const fetchData = async () => {
} }
}; };
//
const onPageChange = (current: number) => {
params.current = current;
fetchData();
};
const changeStatus = () => { const changeStatus = () => {
fetchData(); fetchData();
setVisible(false); setVisible(false);
@ -235,10 +150,11 @@ const handleCreate = () => {
const handleEdit = (item: any) => { const handleEdit = (item: any) => {
isCreate.value = 'false'; isCreate.value = 'false';
formTitle = '修改票据信息'; formTitle = '修改票据信息';
ticketStore.getDetail(item.id).then((res) => { ticketItem = item.id;
console.log('qwe', res); // ticketStore.getDetail(item.id).then((res) => {
}); // console.log('qwe', res);
formData.value = item; // });
// formData.value = item;
setVisible(true); setVisible(true);
}; };
@ -252,56 +168,6 @@ const handleDelete = async (item: any) => {
changeStatus(); changeStatus();
} }
}; };
const handleStorage = async () => {
const valid = await createEditRef.value?.validate();
if (!valid) {
formData.value.status = 'SUBMIT';
if (isCreate.value) {
const res = await ticketStore.createTicket(formData.value);
if (res.status === 200) {
Message.success({
content: '暂存成功',
duration: 5 * 1000,
});
}
} else {
const res = await ticketStore.updateTicket(formData.value);
if (res.status === 200) {
Message.success({
content: '暂存成功',
duration: 5 * 1000,
});
}
}
}
};
const handleOk = async () => {
const valid = await createEditRef.value?.validate();
if (!valid) {
formData.value.status = 'EXAMINE';
if (isCreate.value) {
const res = await ticketStore.createTicket(formData.value);
if (res.status === 200) {
Message.success({
content: `提交成功`,
duration: 5 * 1000,
});
}
} else {
const res = await ticketStore.updateTicket(formData.value);
if (res.status === 200) {
Message.success({
content: `提交成功`,
duration: 5 * 1000,
});
}
}
createEditRef.value?.resetFields();
}
};
</script> </script>
<script lang="ts"> <script lang="ts">

View File

@ -0,0 +1,413 @@
<template>
<a-button
v-if="props.isCreate"
type="primary"
size="small"
:style="{ marginRight: '10px' }"
@click="handleClick"
>
{{ modalTitle }}
</a-button>
<a-button
v-if="!props.isCreate"
size="small"
type="primary"
:style="{ marginRight: '10px' }"
@click="handleClick"
>
{{ modalTitle }}
</a-button>
<a-modal
width="600px"
:visible="visible"
:footer="false"
@cancel="handleCancel"
>
<template #title>{{ modalTitle }}</template>
<a-form ref="createEditRef" :model="formData" :style="{ width: '500px' }">
<a-form-item
field="companyName"
label="公司"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '公司不能为空' }]"
>
<a-input v-model="formData.companyName" />
</a-form-item>
<a-form-item
field="title"
label="标题"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '标题不能为空' }]"
>
<a-input v-model="formData.title" />
</a-form-item>
<a-form-item
field="body"
label="内容"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '内容不能为空' }]"
>
<a-textarea v-model="formData.body" />
</a-form-item>
<a-form-item
field="money"
label="金额"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '金额不能为空' }]"
>
<a-input v-model="formData.money" />
</a-form-item>
<a-form-item
field="contactEmail"
label="联系邮箱"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '联系邮箱不能为空' }]"
>
<a-input v-model="formData.contactEmail" />
</a-form-item>
<a-form-item
field="type"
label="票据类型"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '票据类型不能为空' }]"
>
<a-select
v-model="formData.type"
:options="typesOptions"
:placeholder="$t('searchTable.form.type.placeholder')"
/>
</a-form-item>
<a-form-item field="attachment" label="附件">
<a-upload
:file-list="fileList"
:custom-request="Onchange"
:limit="1"
:on-before-remove="removeAttact"
v-model="formData.attachId"
/>
</a-form-item>
<a-form-item
field="deptId"
label="部门"
:rules="[{ required: true, message: '请选择部门' }]"
:validate-trigger="['change']"
>
<a-tree-select
v-model="formData.deptId"
:field-names="{
key: 'id',
title: 'name',
children: 'children',
}"
:data="deptOptions"
placeholder="请选择部门 ..."
@change="optionDept(true)"
/>
</a-form-item>
<a-form-item field="auditorId" label="审核员">
<a-select v-model="formData.auditorId" placeholder="请选择审核员">
<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 field="status" label="审核状态" v-if="!props.isCreate">
{{ formData.status }}
</a-form-item>
<a-form-item field="comment" label="审核意见" v-if="auditData.comment">
{{ auditData.comment }}
</a-form-item>
<a-form-item>
<a-button type="dashed" @click="handleCancel">取消</a-button>
<a-button
type="outline"
style="margin-left: 15px"
@click="handleStorage"
>暂存</a-button
>
<a-button type="primary" style="margin-left: 15px" @click="handleOk"
>提交</a-button
>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import useVisible from '@/hooks/visible';
import { computed, PropType, ref } from 'vue';
import { computedAsync } from '@vueuse/core';
import { FormInstance } from '@arco-design/web-vue/es/form';
import { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import { Message } from '@arco-design/web-vue';
import { useTicketStore, useUserStore, useRoleStore } from '@/store';
import { TicketRecord, auditRecord } from '@/api/ticket';
import { deptList } from '@/api/dept';
const props = defineProps({
prem: {
type: Object as PropType<TicketRecord>,
},
isCreate: Boolean,
});
const { t } = useI18n();
const ticketStore = useTicketStore();
const userStore = useUserStore();
const roleStore = useRoleStore();
const modalTitle = computed(() => {
return props.isCreate ? '新增' : '修改';
});
const { visible, setVisible } = useVisible(false);
const createEditRef = ref<FormInstance>();
const formData = ref<TicketRecord>({
id: undefined,
title: '',
body: '',
money: undefined,
status: '',
type: '',
contactEmail: '',
companyName: '',
attachId: '',
auditorId: '',
userId: undefined,
submit: '',
comment: '',
});
const auditData = ref<auditRecord>({
auditorId: '',
comment: '',
result: '',
ticketId: '',
});
const emit = defineEmits(['refresh']);
const deptOptions = computedAsync(async () => {
const { data } = await deptList();
return data;
});
const typesOptions = computed<SelectOptionData[]>(() => [
{
label: '银行支票',
value: 'BANK',
},
{
label: '税务支票',
value: 'TAX',
},
{
label: '其他支票',
value: 'OTHER',
},
]);
const statusOptions = computed<SelectOptionData[]>(() => [
{
label: '待审核',
value: 'EXAMINE',
},
{
label: '审核通过',
value: 'PASS',
},
{
label: '审核未通过',
value: 'FAILED',
},
]);
let auiditRoleId = '';
const getRoleId = async () => {
const res = await roleStore.getRoleList();
res.data.forEach((item: any) => {
if (item.name === 'auditor') {
auiditRoleId = item.id;
}
});
};
getRoleId();
const auditorOptions = ref([]);
const optionDept = async (flag: boolean) => {
if (flag) {
formData.value.auditorId = undefined;
}
auditorOptions.value = [];
const res = await userStore.getUserList({
page: 1,
size: 100,
roleId: auiditRoleId,
deptId: formData.value.deptId,
});
auditorOptions.value = res.data.records;
};
let formDifer = {};
const fileList = ref([]);
const Onchange = async (option: any) => {
//
const FormDatas = new FormData();
FormDatas.append('file', option.fileItem.file);
const res = await ticketStore.uploadFileTicket(FormDatas);
if (res.status === 200) {
Message.success({
content: t('上传成功'),
duration: 3 * 1000,
});
res.data.name = res.data.fileName;
fileList.value.push(res.data);
formData.value.attachId = res.data.id;
} else {
Message.error({
content: t('上传失败'),
duration: 3 * 1000,
});
}
};
const removeAttact = () => {
fileList.value.pop();
};
const handleClick = () => {
const ticketId = props.prem?.id;
if (ticketId) {
ticketStore
.getDetail(ticketId)
.then(async (res) => {
formData.value = res.data.bill;
auditData.value = res.data.audit;
if (formData.value.attachId) {
const data = await ticketStore.getAttachment(formData.value.attachId);
data.data.name = data.data.fileName;
fileList.value.push(data.data);
}
const auditInfo = await userStore.getUserDetail(
auditData.value.auditorId
);
formData.value.deptId = auditInfo.data.deptId;
formData.value.auditorId = auditData.value.auditorId;
formDifer = { ...res.data.bill };
})
.then(() => {
optionDept(false).then(() => {
setVisible(true);
});
});
} else {
setVisible(true);
}
};
//
const diffDataForm = (newData: any, oldData: any) => {
const result = {}; //
Object.keys(oldData).forEach((key) => {
if (oldData[key] !== newData[key]) {
result[key] = newData[key];
}
});
return result;
};
const handleOk = async () => {
const valid = await createEditRef.value?.validate();
if (!valid) {
formData.value.submit = true;
formData.value.status = 'EXAMINE';
if (props.isCreate) {
formData.value.userId = userStore.id;
const res = await ticketStore.createTicket(formData.value);
if (res.status === 200) {
Message.success({
content: `添加成功`,
duration: 5 * 1000,
});
}
} else {
formDifer = diffDataForm(formData.value, formDifer);
if (Object.keys(formDifer).length === 0) {
Message.success({
content: `未作任何修改`,
duration: 3 * 1000,
});
} else {
formDifer.id = formData.value.id;
formDifer.submit = true;
const res = await ticketStore.updateTicket(formDifer);
if (res.status === 200) {
Message.success({
content: `提交成功`,
duration: 5 * 1000,
});
}
}
}
createEditRef.value?.resetFields();
removeAttact();
emit('refresh');
setVisible(false);
}
};
const handleStorage = async () => {
const valid = await createEditRef.value?.validate();
if (!valid) {
formData.value.submit = false;
if (props.isCreate) {
const res = await ticketStore.createTicket(formData.value);
if (res.status === 200) {
Message.success({
content: '暂存成功',
duration: 5 * 1000,
});
}
} else {
formDifer = diffDataForm(formData.value, formDifer);
if (Object.keys(formDifer).length === 0) {
Message.success({
content: `未作任何修改`,
duration: 3 * 1000,
});
} else {
formDifer.id = formData.value.id;
formDifer.submit = false;
const res = await ticketStore.updateTicket(formDifer);
if (res.status === 200) {
Message.success({
content: `修改成功`,
duration: 5 * 1000,
});
}
}
}
createEditRef.value?.resetFields();
removeAttact();
emit('refresh');
setVisible(false);
}
};
const handleCancel = async () => {
removeAttact();
createEditRef.value?.resetFields();
setVisible(false);
};
</script>
<style scoped></style>

View File

@ -26,90 +26,87 @@
> >
<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: '500px' }">
<a-form-item <a-form-item field="companyName" label="公司">
field="companyName"
label="公司"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '公司不能为空' }]"
>
<!-- <a-input v-model="formData.companyName" /> -->
<div> <div>
{{ formData.companyName }} {{ formData.companyName }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item field="title" label="标题">
field="title"
label="标题"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '标题不能为空' }]"
>
<!-- <a-input v-model="formData.title" /> -->
<div> <div>
{{ formData.title }} {{ formData.title }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item field="body" label="内容">
field="body"
label="内容"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '内容不能为空' }]"
>
<!-- <a-textarea v-model="formData.body" /> -->
<div> <div>
{{ formData.body }} {{ formData.body }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item field="money" label="金额">
field="money"
label="金额"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '金额不能为空' }]"
>
<!-- <a-input v-model="formData.money" /> -->
<div> <div>
{{ formData.money }} {{ formData.money }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item field="createTime" label="创建时间"> <a-form-item field="createTime" label="创建时间">
<!-- <a-input v-model="formData.createTime" /> -->
<div> <div>
{{ formData.createTime }} {{ dayjs(formData.createTime).format('YYYY-MM-DD') }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item field="contactEmail" label="联系邮箱">
field="contactEmail"
label="联系邮箱"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '联系邮箱不能为空' }]"
>
<!-- <a-input v-model="formData.contactEmail" /> -->
<div> <div>
{{ formData.contactEmail }} {{ formData.contactEmail }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item field="type" label="票据类型">
field="type"
label="票据类型"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '票据类型不能为空' }]"
>
<!-- <a-input v-model="formData.type" /> -->
<div> <div>
{{ formData.type }} {{ formData.type }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item field="attachment" label="附件">
<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="部门">
<a-tree-select
v-model="formData.deptId"
:field-names="{
key: 'id',
title: 'name',
children: 'children',
}"
:data="deptOptions"
disabled
style="color: black"
/>
</a-form-item>
<a-form-item field="auditorId" label="审核员">
<a-select v-model="formData.auditorId" disabled style="color: black">
<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 field="status" label="审核状态" v-if="props.isDetail"> <a-form-item field="status" label="审核状态" v-if="props.isDetail">
<div> <div>
{{ formData.status }} {{ formData.status }}
</div> </div>
</a-form-item> </a-form-item>
<a-form-item <a-form-item
v-else v-else
field="status" field="status"
@ -124,6 +121,11 @@
:placeholder="$t('searchTable.form.status.placeholder')" :placeholder="$t('searchTable.form.status.placeholder')"
/> />
</a-form-item> </a-form-item>
<a-form-item field="comment" label="审核意见">
<a-textarea v-model="auditData.comment" v-if="!props.isDetail" />
<div v-else>{{ auditData.comment }} </div>
</a-form-item>
</a-form> </a-form>
</a-modal> </a-modal>
</template> </template>
@ -131,11 +133,14 @@
<script lang="ts" setup> <script lang="ts" setup>
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';
import { useTicketStore } from '@/store'; import { useTicketStore, useUserStore, useRoleStore } from '@/store';
import { TicketRecord } from '@/api/ticket'; import { TicketRecord, auditRecord } from '@/api/ticket';
import { deptList } from '@/api/dept';
import dayjs from 'dayjs';
const props = defineProps({ const props = defineProps({
prem: { prem: {
@ -143,10 +148,9 @@ const props = defineProps({
}, },
isDetail: Boolean, isDetail: Boolean,
}); });
const userStore = useUserStore();
console.log('props', props);
const ticketStore = useTicketStore(); const ticketStore = useTicketStore();
const roleStore = useRoleStore();
const modalTitle = computed(() => { const modalTitle = computed(() => {
return props.isDetail ? '详情' : '审核'; return props.isDetail ? '详情' : '审核';
}); });
@ -155,23 +159,55 @@ const createEditRef = ref<FormInstance>();
const formData = ref<TicketRecord>({ const formData = ref<TicketRecord>({
id: undefined, id: undefined,
remark: '',
createTime: '',
title: '', title: '',
body: '', body: '',
money: undefined, money: undefined,
status: '',
type: '', type: '',
contactEmail: '', contactEmail: '',
companyName: '', companyName: '',
attachId: '',
auditorId: '',
submit: '',
});
const auditData = ref<auditRecord>({
auditorId: '',
comment: '',
result: '',
ticketId: '',
});
const emit = defineEmits(['refresh']);
const deptOptions = computedAsync(async () => {
const { data } = await deptList();
return data;
}); });
const emit = defineEmits(['refresh']); let auiditRoleId = '';
const getRoleId = async () => {
const res = await roleStore.getRoleList();
res.data.forEach((item: any) => {
if (item.name === 'auditor') {
auiditRoleId = item.id;
}
});
};
getRoleId();
const auditorOptions = ref([]);
const optionDept = async () => {
auditorOptions.value = [];
const res = await userStore.getUserList({
page: 1,
size: auiditRoleId,
roleId: '54',
deptId: formData.value.deptId,
});
auditorOptions.value = res.data.records;
};
const statusOptions = computed<SelectOptionData[]>(() => [ const statusOptions = computed<SelectOptionData[]>(() => [
{ // {
label: '待审核', // label: '',
value: 'EXAMINE', // value: 'EXAMINE',
}, // },
{ {
label: '审核通过', label: '审核通过',
value: 'PASS', value: 'PASS',
@ -181,17 +217,37 @@ const statusOptions = computed<SelectOptionData[]>(() => [
value: 'FAILED', value: 'FAILED',
}, },
]); ]);
const fileList = ref([]);
const handleClick = () => { const handleClick = async () => {
const ticketId = props.prem?.id; const ticketId = props.prem?.id;
ticketStore ticketStore
.getDetail(ticketId) .getDetail(ticketId)
.then((res) => { .then(async (res) => {
formData.value = res.data.bill; formData.value = res.data.bill;
if (!props.isDetail) {
if (formData.value.status === '审核通过') {
formData.value.status = 'PASS';
} else if (formData.value.status === '审核未通过') {
formData.value.status = 'FAILED';
}
}
auditData.value = res.data.audit;
if (formData.value.attachId) {
const data = await ticketStore.getAttachment(formData.value.attachId);
data.data.name = data.data.fileName;
fileList.value.push(data.data);
}
const auditInfo = await userStore.getUserDetail(
auditData.value.auditorId
);
formData.value.deptId = auditInfo.data.deptId;
formData.value.auditorId = auditData.value.auditorId;
}) })
.then(() => { .then(() => {
optionDept().then(() => {
setVisible(true); setVisible(true);
}); });
});
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
@ -200,7 +256,11 @@ const handleSubmit = async () => {
if (props.isDetail) { if (props.isDetail) {
createEditRef.value?.resetFields(); createEditRef.value?.resetFields();
} else { } else {
const res = await ticketStore.updateTicket(formData.value); auditData.value.result = formData.value.status;
const res = await ticketStore.auditTicket(
formData.value.id,
auditData.value
);
if (res.status === 200) { if (res.status === 200) {
Message.success({ Message.success({
content: `${modalTitle.value}成功`, content: `${modalTitle.value}成功`,
@ -208,12 +268,14 @@ const handleSubmit = async () => {
}); });
} }
} }
fileList.value.pop();
emit('refresh'); emit('refresh');
setVisible(false); setVisible(false);
} }
}; };
const handleCancel = async () => { const handleCancel = async () => {
fileList.value.pop();
createEditRef.value?.resetFields(); createEditRef.value?.resetFields();
setVisible(false); setVisible(false);
}; };

View File

@ -73,12 +73,13 @@
<a-row style="margin-bottom: 16px"> <a-row style="margin-bottom: 16px">
<a-col :span="12"> <a-col :span="12">
<a-space> <a-space>
<!-- <DeptEdit <TicketForm
ref="createRef" ref="createEditRef"
:trees="renderData" :prem="ticketItem"
:is-create="true" :is-create="true"
@refresh="search" @refresh="search"
/> --> v-if="userStore.permissions !== 'auditor'"
/>
</a-space> </a-space>
</a-col> </a-col>
<a-col <a-col
@ -167,22 +168,48 @@
/> />
</template> </template>
<template #operations="{ record }"> <template #operations="{ record }">
<!-- 详情 -->
<TicketEdit <TicketEdit
ref="editRef" ref="editRef"
:prem="record" :prem="record"
:is-detail="true" :is-detail="true"
@refresh="search" @refresh="search"
v-if="
userStore.permissions === 'admin' ||
(userStore.permissions === 'user' &&
(record.status == '审核未通过' || record.status == '待提交'
? false
: true))
"
/> />
<!-- <TicketEdit <!-- 修改 -->
<TicketForm
ref="createEditRef"
:prem="record"
:is-create="false"
@refresh="search"
v-if="
userStore.permissions == 'user' &&
(record.status == '审核未通过' || record.status == '待提交'
? true
: false)
"
/>
<!-- 审核 -->
<TicketEdit
ref="editRef" ref="editRef"
:prem="record" :prem="record"
:is-detail="false" :is-detail="false"
@refresh="search" @refresh="search"
/> --> v-if="userStore.permissions === 'auditor'"
/>
<a-popconfirm <a-popconfirm
content="确认删除此部门?" content="确认删除此部门?"
type="error" type="error"
@ok="handleDelete(record)" @ok="handleDelete(record)"
v-if="userStore.permissions === 'admin'"
> >
<a-button type="primary" size="small" status="danger"> <a-button type="primary" size="small" status="danger">
删除 删除
@ -204,12 +231,15 @@ 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 dayjs from 'dayjs'; import dayjs from 'dayjs';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import { useTicketStore } from '@/store'; import { useTicketStore, useUserStore } from '@/store';
import { TicketRecord } from '@/api/ticket'; import { TicketRecord } from '@/api/ticket';
import TicketEdit from './components/ticket-edit.vue'; import TicketEdit from './components/ticket-edit.vue';
import TicketForm from './components/form-edit.vue';
const { loading, setLoading } = useLoading(true); const { loading, setLoading } = useLoading(true);
const ticketStore = useTicketStore(); const ticketStore = useTicketStore();
const userStore = useUserStore();
const { const {
cloneColumns, cloneColumns,
showColumns, showColumns,
@ -270,20 +300,24 @@ const columns = computed<TableColumnData[]>(() => [
const typesOptions = computed<SelectOptionData[]>(() => [ const typesOptions = computed<SelectOptionData[]>(() => [
{ {
label: 'Bank', label: '银行支票',
value: 'Bank', value: 'BANK',
}, },
{ {
label: 'Tax', label: '税务支票',
value: 'Tax', value: 'TAX',
}, },
{ {
label: 'Other', label: '其他支票',
value: 'Other', value: 'OTHER',
}, },
]); ]);
const statusOptions = computed<SelectOptionData[]>(() => [ const statusOptions = computed<SelectOptionData[]>(() => [
{
label: '待提交',
value: 'SUBMIT',
},
{ {
label: '待审核', label: '待审核',
value: 'EXAMINE', value: 'EXAMINE',
@ -315,10 +349,23 @@ const onPageChange = (current: number) => {
}; };
// //
const fetchData = async (params: { page: 1; current: 1; size: 10 }) => { const fetchData = async (params: {
userId: number | undefined;
page: 1;
current: 1;
size: 10;
}) => {
setLoading(true); setLoading(true);
try { try {
const res = await ticketStore.getTicketList(params); let res = {};
if (userStore.permissions === 'admin') {
res = await ticketStore.getTicketList(params);
} else if (userStore.permissions === 'auditor') {
res = await ticketStore.auditTickctList(params);
} else if (userStore.permissions === 'user') {
params.userId = userStore.id;
res = await ticketStore.getTicketList(params);
}
renderData.value = res.data.records; renderData.value = res.data.records;
pagination.page = params.page; pagination.page = params.page;
pagination.current = params.current; pagination.current = params.current;

View File

@ -83,7 +83,7 @@ import { ref } from 'vue';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { FormInstance } from '@arco-design/web-vue/es/form'; import { FormInstance } from '@arco-design/web-vue/es/form';
import { UserState } from '@/store/modules/user/types'; import { UserState } from '@/store/modules/user/types';
import { selfUpdate } from '@/api/user'; import { selfUpdate, getUserInfo } from '@/api/user';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
const userStore = useUserStore(); const userStore = useUserStore();
@ -99,6 +99,7 @@ const validate = async () => {
// you also can use html-type to submit // you also can use html-type to submit
const res = await selfUpdate(formData.value); const res = await selfUpdate(formData.value);
if (res.status === 200) { if (res.status === 200) {
await userStore.info();
Message.success({ Message.success({
content: '编辑成功', content: '编辑成功',
duration: 5 * 1000, duration: 5 * 1000,

View File

@ -95,13 +95,7 @@
const controller = new AbortController(); const controller = new AbortController();
(async function requestWrap() { (async function requestWrap() {
const { const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options;
onProgress,
onError,
onSuccess,
fileItem,
name = 'file',
} = options;
onProgress(20); onProgress(20);
const formData = new FormData(); const formData = new FormData();
formData.append(name as string, fileItem.file as Blob); formData.append(name as string, fileItem.file as Blob);