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

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 { Message, Modal } from '@arco-design/web-vue';
import { getToken } from '@/utils/auth';
import { useUserStore } from '@/store';
export interface HttpResponse<T = unknown> {
status: number;

View File

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

View File

@ -1,19 +1,28 @@
import axios from 'axios';
export interface TicketCreateRecord {
remark: string;
createTime: string;
title: string;
body: string;
money: undefined;
status: string;
type: string;
contactEmail: string;
companyName: string;
attachId: string;
auditorId: string;
submit: boolean;
userId: undefined;
}
export interface TicketRecord extends TicketCreateRecord {
value: any;
id: undefined;
status: string
}
export interface auditRecord {
auditorId: string;
comment: string;
result: string;
ticketId: string;
}
export function queryTicket(data: any) {
@ -28,9 +37,10 @@ export function getDetail(id: string) {
return axios.get(`/api/rest/bill/${id}`);
}
// 更新票据
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) {
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 {
username: string;
nickName: string;
password: string;
@ -39,8 +40,9 @@ export interface CreateRecord {
enabled: string;
address: string;
deptId: DeptRecord | undefined;
roleId: RoleRecord | undefined;
roleId: string| RoleRecord | undefined;
permissionIds: (number | undefined)[];
authorities: string[];
}
export interface SelfRecord {
@ -53,12 +55,16 @@ export interface SelfRecord {
// 用户数据
export interface UserRecord extends CreateRecord {
id: number;
value: any;
id: string;
avatar: string;
createAt: string
}
export interface UserParams extends Partial<UserRecord> {
page: number;
size: number;
current: number
}
export interface Pageable {
@ -108,14 +114,20 @@ export function create(data: CreateRecord) {
}
// 模糊查询用户列表
export function queryUserList(params: UserParams) {
export function queryUserList(params: any) {
return axios({
url: '/api/rest/user/query',
url: '/api/rest/user',
params,
method: 'get',
});
}
// 根据id查询用户信息
export function userDetail(id: string){
return axios.get(`/api/rest/user/${id}`)
}
// 是否启用
export function enabled(id: string) {
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);
}
export function switchRole(roleId: number) {
return axios.patch<UserState>(`/api/user/self/switch-role/${roleId}`);
}
// 获取个人用户信息
export function getUserInfo() {
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) {
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 userIcon from '@/assets/images/user-circle.png';
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';
const router = useRouter();
const appStore = useAppStore();
const userStore = useUserStore();
const { logout } = useUser();
@ -257,7 +260,12 @@ const setPopoverVisible = () => {
refBtn.value.dispatchEvent(event);
};
const handleLogout = () => {
logout();
// logout();
clearToken();
router.push({
name: 'login',
});
};
const setDropDownVisible = () => {
const event = new MouseEvent('click', {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,11 +2,17 @@ import { defineStore } from 'pinia';
import {
TicketRecord,
TicketCreateRecord,
auditRecord,
queryTicket,
remove,
getDetail,
create,
update,
uploadFile,
attachment,
audit,
auditTickctList,
} from '@/api/ticket';
import { ticketStore } from './type';
@ -22,6 +28,7 @@ const useTicketStore = defineStore('ticket', {
type: undefined,
contactEmail: undefined,
companyName: undefined,
userId: undefined,
}),
getters: {
@ -49,6 +56,22 @@ const useTicketStore = defineStore('ticket', {
async updateTicket(data: TicketRecord) {
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;
contactEmail?: string;
companyName?: string;
userId?: undefined;
}

View File

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

View File

@ -2,6 +2,7 @@ import { RoleRecord } from '@/api/role';
export type RoleType = '' | '*' | 'admin' | 'user' | string[];
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;
nickName?: string;
avatar?: string;
@ -13,5 +14,6 @@ export interface UserState {
id?: number;
role?: RoleRecord;
roles?: RoleRecord[];
permissions?: string[] | '' | '*' | 'admin' | 'user';
permissions?: string[] | '' | '*' | 'admin' | 'user'|'auditor';
authorities?: string[]
}

View File

@ -51,21 +51,100 @@
>
{{ $t('login.form.rememberPassword') }}
</a-checkbox>
<a-link>{{ $t('login.form.forgetPassword') }}</a-link>
<!-- <a-link>{{ $t('login.form.forgetPassword') }}</a-link> -->
</div>
<a-button type="primary" html-type="submit" long :loading="loading">
{{ $t('login.form.login') }}
</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') }}
</a-button>
</a-space>
</a-form>
</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>
<script lang="ts" setup>
import useVisible from '@/hooks/visible';
import { FormInstance } from '@arco-design/web-vue/es/form';
import { ref, reactive } from 'vue';
import { useRouter } from 'vue-router';
import { Message } from '@arco-design/web-vue';
@ -74,14 +153,35 @@ import { useI18n } from 'vue-i18n';
import { useStorage } from '@vueuse/core';
import { useUserStore } from '@/store';
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 { t } = useI18n();
const errorMessage = ref('');
const { loading, setLoading } = useLoading();
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', {
// 使 useStorage
rememberPassword: true,
@ -114,7 +214,7 @@ const handleSubmit = async ({
const res = await userStore.me();
await userStore.login(values as LoginData, res.data.csrf.token);
const csrf = await userStore.me();
await userStore.info();
//
const { redirect, ...othersQuery } = router.currentRoute.value.query;
router.push({
@ -144,6 +244,30 @@ const handleSubmit = async ({
const setRememberPassword = (value: boolean) => {
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>
<style lang="less" scoped>

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,15 @@
<template>
<a-space width="450px" v-if="!props.isCreate">
<a-form ref="createEditRef" :model="formData" :style="{ width: '450px' }">
<a-space 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
field="companyName"
label="公司"
@ -37,10 +46,6 @@
<a-input v-model="formData.money" />
</a-form-item>
<a-form-item field="createTime" label="创建时间">
<a-input v-model="formData.createTime" />
</a-form-item>
<a-form-item
field="contactEmail"
label="联系邮箱"
@ -56,41 +61,73 @@
:validate-trigger="['change', 'input']"
: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 field="status" label="审核状态" v-if="props.isDetail">
<div>
{{ formData.status }}
</div>
</a-form-item>
<a-form-item
v-else
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 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"
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-space>
</template>
<script lang="ts" setup>
import { useI18n } from 'vue-i18n';
import useVisible from '@/hooks/visible';
import { computed, PropType, ref, watch } from 'vue';
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 { Message } from '@arco-design/web-vue';
import useTableOption from '@/hooks/table-option';
import { useTicketStore } from '@/store';
import { useTicketStore, useUserStore } from '@/store';
import { TicketRecord } from '@/api/ticket';
import { deptList } from '@/api/dept';
const props = defineProps({
prem: {
@ -100,27 +137,47 @@ const props = defineProps({
isVisible: Boolean,
});
console.log('ef', props);
const { t } = useI18n();
const ticketStore = useTicketStore();
const modalTitle = computed(() => {
return props.isCreate ? '详情' : '审核';
const userStore = useUserStore();
const formTitle = computed(() => {
return props.isCreate ? '添加票据' : '修改票据信息';
});
const { visible, setVisible } = useVisible(true);
const createEditRef = ref<FormInstance>();
const formData = ref<TicketRecord>({
id: undefined,
remark: '',
createTime: '',
title: '',
body: '',
money: undefined,
status: '',
type: '',
contactEmail: '',
companyName: '',
attachId: '',
auditorId: '',
submit: '',
});
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: '待审核',
@ -135,43 +192,97 @@ const statusOptions = computed<SelectOptionData[]>(() => [
value: 'FAILED',
},
]);
const handleClick = () => {
const ticketId = props.prem?.id;
ticketStore
.getDetail(ticketId)
.then((res) => {
formData.value = res.data.bill;
})
.then(() => {
setVisible(true);
const auditorOptions = ref([]);
const optionDept = async () => {
auditorOptions.value = [];
const res = await userStore.getUserList({
page: 1,
size: 100,
roleId: '54',
deptId: formData.value.deptId,
});
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();
if (!valid) {
formData.value.submit = false;
if (props.isCreate) {
createEditRef.value?.resetFields();
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: `${modalTitle.value}成功`,
content: '暂存成功',
duration: 5 * 1000,
});
}
}
emit('refresh');
setVisible(false);
createEditRef.value?.resetFields();
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();
setVisible(false);
removeAttact();
}
};
</script>

View File

@ -16,15 +16,17 @@
type="outline"
size="small"
@click="handleCreate"
style="margin-left: 70px"
style="margin-left: 120px"
>新建</a-button
>
<a-list
:style="{ width: `300px` }"
:style="{ width: `350px` }"
:virtualListProps="{
height: 550,
}"
:data="uncommitData"
:pagination-props="paginationProps"
@page-change="onPageChange"
>
<template #item="{ item, id }">
<a-list-item :key="id">
@ -39,89 +41,12 @@
</a-col>
<a-divider style="height: 600px" direction="vertical" />
<a-col :flex="3">
<!-- <TicketForm
<TicketForm
ref="createEditRef"
:prem="ticketItem"
:is-create="false"
:is-visible="true"
/> -->
<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')"
:is-create="true"
:is-visible="visible"
/>
</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-row>
</a-card>
@ -133,83 +58,67 @@ import { computed, ref, watch, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import useLoading from '@/hooks/loading';
import { Pagination } from '@/types/global';
import { computedAsync } from '@vueuse/core';
import type { SelectOptionData } from '@arco-design/web-vue/es/select/interface';
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface';
import useTableOption from '@/hooks/table-option';
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 useVisible from '@/hooks/visible';
import { useTicketStore } from '@/store';
import { useTicketStore, useUserStore, useDeptStore } from '@/store';
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 { loading, setLoading } = useLoading(true);
const ticketStore = useTicketStore();
const userStore = useUserStore();
const deptStore = useDeptStore();
const { t } = useI18n();
const uncommitData = ref<TicketRecord[]>([]);
const ticketStatus = ref('SUBMIT');
const renderData = () => {
return {
id: undefined,
remark: '',
createTime: '',
title: '',
body: '',
money: undefined,
status: '',
type: '',
contactEmail: '',
companyName: '',
attachId: '',
auditorId: '',
submit: Boolean,
};
};
const formData = ref(renderData());
const { visible, setVisible } = useVisible(false);
let formTitle = '新建票据';
const isCreate = ref('false');
const typesOptions = computed<SelectOptionData[]>(() => [
{
label: 'Bank',
value: 'Bank',
},
{
label: 'Tax',
value: 'Tax',
},
{
label: 'Other',
value: 'Other',
},
]);
const statusOptions = computed<SelectOptionData[]>(() => [
{
label: '待审核',
value: 'EXAMINE',
},
{
label: '审核通过',
value: 'PASS',
},
{
label: '审核未通过',
value: 'FAILED',
},
]);
const isCreate = ref('true');
const paginationProps = reactive({
defaultPageSize: 10,
total: 10,
});
let ticketItem = '';
const params = {
page: 1,
current: 1,
size: 10,
status: ticketStatus.value,
};
//
const fetchData = async () => {
setLoading(true);
const params = {
page: 1,
current: 1,
size: 20,
status: ticketStatus.value,
};
try {
ticketStore.getTicketList(params).then((res) => {
uncommitData.value = res.data.records;
paginationProps.defaultPageSize = params.size;
paginationProps.total = res.data.total;
});
} catch (err) {
// you can report use errorHandler or other
@ -218,6 +127,12 @@ const fetchData = async () => {
}
};
//
const onPageChange = (current: number) => {
params.current = current;
fetchData();
};
const changeStatus = () => {
fetchData();
setVisible(false);
@ -235,10 +150,11 @@ const handleCreate = () => {
const handleEdit = (item: any) => {
isCreate.value = 'false';
formTitle = '修改票据信息';
ticketStore.getDetail(item.id).then((res) => {
console.log('qwe', res);
});
formData.value = item;
ticketItem = item.id;
// ticketStore.getDetail(item.id).then((res) => {
// console.log('qwe', res);
// });
// formData.value = item;
setVisible(true);
};
@ -252,56 +168,6 @@ const handleDelete = async (item: any) => {
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 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>
<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 field="companyName" label="公司">
<div>
{{ formData.companyName }}
</div>
</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 field="title" label="标题">
<div>
{{ formData.title }}
</div>
</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 field="body" label="内容">
<div>
{{ formData.body }}
</div>
</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 field="money" label="金额">
<div>
{{ formData.money }}
</div>
</a-form-item>
<a-form-item field="createTime" label="创建时间">
<!-- <a-input v-model="formData.createTime" /> -->
<div>
{{ formData.createTime }}
{{ dayjs(formData.createTime).format('YYYY-MM-DD') }}
</div>
</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 field="contactEmail" label="联系邮箱">
<div>
{{ formData.contactEmail }}
</div>
</a-form-item>
<a-form-item
field="type"
label="票据类型"
:validate-trigger="['change', 'input']"
:rules="[{ required: true, message: '票据类型不能为空' }]"
>
<!-- <a-input v-model="formData.type" /> -->
<a-form-item field="type" label="票据类型">
<div>
{{ formData.type }}
</div>
</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">
<div>
{{ formData.status }}
</div>
</a-form-item>
<a-form-item
v-else
field="status"
@ -124,6 +121,11 @@
:placeholder="$t('searchTable.form.status.placeholder')"
/>
</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-modal>
</template>
@ -131,11 +133,14 @@
<script lang="ts" setup>
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 } from '@/store';
import { TicketRecord } from '@/api/ticket';
import { useTicketStore, useUserStore, useRoleStore } from '@/store';
import { TicketRecord, auditRecord } from '@/api/ticket';
import { deptList } from '@/api/dept';
import dayjs from 'dayjs';
const props = defineProps({
prem: {
@ -143,10 +148,9 @@ const props = defineProps({
},
isDetail: Boolean,
});
console.log('props', props);
const userStore = useUserStore();
const ticketStore = useTicketStore();
const roleStore = useRoleStore();
const modalTitle = computed(() => {
return props.isDetail ? '详情' : '审核';
});
@ -155,23 +159,55 @@ const createEditRef = ref<FormInstance>();
const formData = ref<TicketRecord>({
id: undefined,
remark: '',
createTime: '',
title: '',
body: '',
money: undefined,
status: '',
type: '',
contactEmail: '',
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[]>(() => [
{
label: '待审核',
value: 'EXAMINE',
},
// {
// label: '',
// value: 'EXAMINE',
// },
{
label: '审核通过',
value: 'PASS',
@ -181,17 +217,37 @@ const statusOptions = computed<SelectOptionData[]>(() => [
value: 'FAILED',
},
]);
const handleClick = () => {
const fileList = ref([]);
const handleClick = async () => {
const ticketId = props.prem?.id;
ticketStore
.getDetail(ticketId)
.then((res) => {
.then(async (res) => {
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(() => {
optionDept().then(() => {
setVisible(true);
});
});
};
const handleSubmit = async () => {
@ -200,7 +256,11 @@ const handleSubmit = async () => {
if (props.isDetail) {
createEditRef.value?.resetFields();
} 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) {
Message.success({
content: `${modalTitle.value}成功`,
@ -208,12 +268,14 @@ const handleSubmit = async () => {
});
}
}
fileList.value.pop();
emit('refresh');
setVisible(false);
}
};
const handleCancel = async () => {
fileList.value.pop();
createEditRef.value?.resetFields();
setVisible(false);
};

View File

@ -73,12 +73,13 @@
<a-row style="margin-bottom: 16px">
<a-col :span="12">
<a-space>
<!-- <DeptEdit
ref="createRef"
:trees="renderData"
<TicketForm
ref="createEditRef"
:prem="ticketItem"
:is-create="true"
@refresh="search"
/> -->
v-if="userStore.permissions !== 'auditor'"
/>
</a-space>
</a-col>
<a-col
@ -167,22 +168,48 @@
/>
</template>
<template #operations="{ record }">
<!-- 详情 -->
<TicketEdit
ref="editRef"
:prem="record"
:is-detail="true"
@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"
:prem="record"
:is-detail="false"
@refresh="search"
/> -->
v-if="userStore.permissions === 'auditor'"
/>
<a-popconfirm
content="确认删除此部门?"
type="error"
@ok="handleDelete(record)"
v-if="userStore.permissions === 'admin'"
>
<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 dayjs from 'dayjs';
import { Message } from '@arco-design/web-vue';
import { useTicketStore } from '@/store';
import { useTicketStore, useUserStore } from '@/store';
import { TicketRecord } from '@/api/ticket';
import TicketEdit from './components/ticket-edit.vue';
import TicketForm from './components/form-edit.vue';
const { loading, setLoading } = useLoading(true);
const ticketStore = useTicketStore();
const userStore = useUserStore();
const {
cloneColumns,
showColumns,
@ -270,20 +300,24 @@ const columns = computed<TableColumnData[]>(() => [
const typesOptions = computed<SelectOptionData[]>(() => [
{
label: 'Bank',
value: 'Bank',
label: '银行支票',
value: 'BANK',
},
{
label: 'Tax',
value: 'Tax',
label: '税务支票',
value: 'TAX',
},
{
label: 'Other',
value: 'Other',
label: '其他支票',
value: 'OTHER',
},
]);
const statusOptions = computed<SelectOptionData[]>(() => [
{
label: '待提交',
value: 'SUBMIT',
},
{
label: '待审核',
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);
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;
pagination.page = params.page;
pagination.current = params.current;

View File

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

View File

@ -35,40 +35,40 @@
}"
>
<template #label="{ label }">{{ $t(label) }} :</template>
<!-- <template #value="{ value, data }">-->
<!-- <a-tag-->
<!-- v-if="data.label === 'userSetting.label.certification'"-->
<!-- color="green"-->
<!-- size="small"-->
<!-- >-->
<!-- 已认证-->
<!-- </a-tag>-->
<!-- <span v-else>{{ value }}</span>-->
<!-- </template>-->
<!-- <template #value="{ value, data }">-->
<!-- <a-tag-->
<!-- v-if="data.label === 'userSetting.label.certification'"-->
<!-- color="green"-->
<!-- size="small"-->
<!-- >-->
<!-- 已认证-->
<!-- </a-tag>-->
<!-- <span v-else>{{ value }}</span>-->
<!-- </template>-->
</a-descriptions>
</a-space>
</a-card>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import type {
import { ref } from 'vue';
import type {
FileItem,
RequestOption,
} from '@arco-design/web-vue/es/upload/interfaces';
import { useUserStore } from '@/store';
import { userUploadApi } from '@/api/user-center';
import userIcon from '@/assets/images/user-circle.png';
import type { DescData } from '@arco-design/web-vue/es/descriptions/interface';
import dayjs from 'dayjs';
} from '@arco-design/web-vue/es/upload/interfaces';
import { useUserStore } from '@/store';
import { userUploadApi } from '@/api/user-center';
import userIcon from '@/assets/images/user-circle.png';
import type { DescData } from '@arco-design/web-vue/es/descriptions/interface';
import dayjs from 'dayjs';
const userStore = useUserStore();
const file = {
const userStore = useUserStore();
const file = {
uid: '-2',
name: 'avatar.png',
url: userStore.avatar ? userStore.avatar : userIcon,
};
const renderData = [
};
const renderData = [
{
label: 'userSetting.label.name',
value: userStore.username,
@ -85,23 +85,17 @@
label: 'userSetting.label.registrationDate',
value: dayjs(userStore.createAt).format('YYYY-MM-DD hh:mm:ss'),
},
] as DescData[];
const fileList = ref<FileItem[]>([file]);
const uploadChange = (fileItemList: FileItem[], fileItem: FileItem) => {
] as DescData[];
const fileList = ref<FileItem[]>([file]);
const uploadChange = (fileItemList: FileItem[], fileItem: FileItem) => {
fileList.value = [fileItem];
};
const customRequest = (options: RequestOption) => {
};
const customRequest = (options: RequestOption) => {
// docs: https://axios-http.com/docs/cancellation
const controller = new AbortController();
(async function requestWrap() {
const {
onProgress,
onError,
onSuccess,
fileItem,
name = 'file',
} = options;
const { onProgress, onError, onSuccess, fileItem, name = 'file' } = options;
onProgress(20);
const formData = new FormData();
formData.append(name as string, fileItem.file as Blob);
@ -131,15 +125,15 @@
controller.abort();
},
};
};
};
</script>
<style scoped lang="less">
.arco-card {
.arco-card {
padding: 14px 0 4px 4px;
border-radius: 4px;
}
:deep(.arco-avatar-trigger-icon-button) {
}
:deep(.arco-avatar-trigger-icon-button) {
width: 32px;
height: 32px;
line-height: 32px;
@ -149,5 +143,5 @@
color: rgb(var(--arcoblue-6));
font-size: 14px;
}
}
}
</style>