协同办公优化

This commit is contained in:
z9130 2024-09-24 17:39:51 +08:00
parent 0149f4b10b
commit 3b1197f9ec
30 changed files with 1972 additions and 1179 deletions

View File

@ -1,101 +1,119 @@
<script setup lang="ts">
import { ref, computed, reactive, onMounted } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { useVxeTable } from '#/hooks/vxeTable';
import { MdiAdd, MdiUpdate, MdiDelete, MdiImport, MdiExport, MdiRadioUnchecked, MdiRadioChecked } from '@vben/icons';
import { getFormSchema, getColumns, PrimaryKey } from './crud.tsx';
import { getMonthStartAndEnd } from '#/utils/time'
import Apis from '#/api'
import dayjs from 'dayjs';
import { message } from "ant-design-vue";
import { Modal } from 'ant-design-vue';
import { computed, onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useRouter } from 'vue-router'
import { Page, useVbenModal } from '@vben/common-ui';
import {
MdiAdd,
MdiDelete,
MdiExport,
MdiRadioChecked,
MdiRadioUnchecked,
MdiUpdate,
} from '@vben/icons';
import { message, Modal } from 'ant-design-vue';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { getMonthStartAndEnd } from '#/utils/time';
import { getColumns, getFormSchema, PrimaryKey } from './crud.tsx';
const router = useRouter();
const checkedValue = ref('all')
const checkedValue = ref('all');
const exportSearchParams = ref<any>({
daterange: getMonthStartAndEnd(),
});
const searchRef = ref()
let isConfirmLoading = ref(false)
const searchRef = ref();
const isConfirmLoading = ref(false);
const [_Modal, modalApi] = useVbenModal({
async onConfirm() {
isConfirmLoading.value = true
isConfirmLoading.value = true;
try {
let params = {};
if (checkedValue.value == "daterange") {
if (checkedValue.value == 'daterange') {
params = {
startDate: exportSearchParams.value.daterange[0],
endDate: exportSearchParams.value.daterange[1],
};
}
let res = await Apis.zbgl.post_export({
params: params, config: {
meta: {
responseType: 'blob'
}
}
}).send();
message.success("导出成功");
modalApi.close()
const res = await Apis.zbgl
.post_export({
params,
config: {
meta: {
responseType: 'blob',
},
},
})
.send();
message.success('导出成功');
modalApi.close();
showExportModal.value = false;
} catch (error) {
console.error(error);
} finally {
isConfirmLoading.value = false
isConfirmLoading.value = false;
}
console.info("onConfirm");
console.info('onConfirm');
},
});
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const treeData = ref([]);
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
columns: getColumns(),
proxyConfig: {
autoLoad: false,
ajax: {
query: ({ page }) => {
return Apis.contractBaseInfo.get_page({ params: { pageNum: page.currentPage, pageSize: page.pageSize, ...searchRef.value?.formData } })
}
const gridOptions = reactive(
gridProps({
columns: getColumns(),
proxyConfig: {
autoLoad: false,
ajax: {
query: ({ page }) => {
return Apis.contractBaseInfo.get_page({
params: {
pageNum: page.currentPage,
pageSize: page.pageSize,
...searchRef.value?.formData,
},
});
},
},
},
},
pagerConfig: {
enabled: true
},
toolbarConfig: {
enabled: true
},
}));
pagerConfig: {
enabled: true,
},
toolbarConfig: {
enabled: true,
},
}),
);
function handleEdit(record?: any) {
if (record && record[PrimaryKey]) {
router.push("/contract/approval/edit/" + record[PrimaryKey]);
router.push(`/contract/approval/edit/${record[PrimaryKey]}`);
} else {
router.push("/contract/approval/edit");
router.push('/contract/approval/edit');
}
}
function handleDelete(row) {
Modal.confirm({
title: '提示',
content: "是否确认删除该条记录?",
content: '是否确认删除该条记录?',
okType: 'danger',
onOk: async () => {
await Apis.contractBaseInfo.post_deletes({ params: { ids: row[PrimaryKey] } })
message.success("删除成功");
triggerProxy("reload");
await Apis.contractBaseInfo.post_deletes({
params: { ids: row[PrimaryKey] },
});
message.success('删除成功');
triggerProxy('reload');
},
onCancel() {
console.log('Cancel');
@ -107,9 +125,9 @@ function handleExport() {
const $grid = xGridRef.value;
if ($grid) {
$grid.exportData({
type: "xlsx",
type: 'xlsx',
});
message.success("导出成功");
message.success('导出成功');
}
}
@ -133,66 +151,81 @@ function handleCellClick({ row }) {
}
onMounted(() => {
triggerProxy('reload')
})
triggerProxy('reload');
});
const searchForm = ref({
...getFormSchema(),
onSearch(context: any) {
triggerProxy('reload')
}
triggerProxy('reload');
},
});
function toPage() {
window.open("/iframe/meeting/standing-book", "_blank");
window.open('/iframe/meeting/standing-book', '_blank');
}
function toDetail(row) {
window.open("/iframe/meeting/start/" + row.guid, "_blank");
window.open(`/iframe/meeting/start/${row.guid}`, '_blank');
}
//
</script>
<template>
<Page contentClass="h-full flex flex-col">
<Page content-class="h-full flex flex-col">
<fs-search ref="searchRef" v-bind="searchForm">
<template #search_price="{ row }">
<a-input-number v-model:value="row.budgetSum1" placeholder="">
</a-input-number>
<a-input-number v-model:value="row.budgetSum1" placeholder="" />
<span class="mx-1"></span>
<a-input-number v-model:value="row.budgetSum2" placeholder="">
</a-input-number>
<a-input-number v-model:value="row.budgetSum2" placeholder="" />
</template>
<template #form_time="{ row }">
<a-date-picker v-model:value="row.startTime" placeholder="" format="YYYY-MM-DD" value-format="YYYY-MM-DD">
</a-date-picker>
<a-date-picker
v-model:value="row.startTime"
format="YYYY-MM-DD"
placeholder=""
value-format="YYYY-MM-DD"
/>
<span class="mx-1"></span>
<a-date-picker v-model:value="row.endTime" placeholder="" format="YYYY-MM-DD" value-format="YYYY-MM-DD">
</a-date-picker>
<a-date-picker
v-model:value="row.endTime"
format="YYYY-MM-DD"
placeholder=""
value-format="YYYY-MM-DD"
/>
</template>
</fs-search>
<div class="flex-1 min-h-300px">
<vxe-grid ref="xGridRef" v-bind="gridOptions" @cell-click="handleCellClick">
<div class="min-h-300px flex-1">
<vxe-grid
ref="xGridRef"
v-bind="gridOptions"
@cell-click="handleCellClick"
>
<template #toolbar_buttons>
<a-space>
<vben-button variant="primary" @click="handleEdit()">
<MdiAdd class="text-lg mr-0.5" />
<MdiAdd class="mr-0.5 text-lg" />
新增
</vben-button>
<vben-button variant="warning" :disabled="!selectRow || !selectRow[PrimaryKey]"
@click="handleEdit(selectRow)">
<MdiUpdate class="text-lg mr-0.5" />
<vben-button
:disabled="!selectRow || !selectRow[PrimaryKey]"
variant="warning"
@click="handleEdit(selectRow)"
>
<MdiUpdate class="mr-0.5 text-lg" />
修改
</vben-button>
<vben-button variant="primary" @click="handleExport()">
<MdiExport class="text-lg mr-0.5" />
<MdiExport class="mr-0.5 text-lg" />
导出
</vben-button>
<vben-button variant="destructive" :disabled="!selectRow || !selectRow[PrimaryKey]"
@click="handleDelete(selectRow)">
<MdiDelete class="text-lg mr-0.5" />
<vben-button
:disabled="!selectRow || !selectRow[PrimaryKey]"
variant="destructive"
@click="handleDelete(selectRow)"
>
<MdiDelete class="mr-0.5 text-lg" />
删除
</vben-button>
</a-space>
@ -204,7 +237,6 @@ function toDetail(row) {
<MdiRadioUnchecked v-else />
</span>
</template>
</vxe-grid>
</div>
</Page>

View File

@ -161,6 +161,9 @@ export default {
get_toDoPage: (data?: QueryOptions) => http.get('/app/ccsq/toDoPage', data),
/** 协同办公/出差申请 已办 */
get_donePage: (data?: QueryOptions) => http.get('/app/ccsq/donePage', data),
/** 协同办公/出差申请 获取可退回节点信息 */
get_getBackNode: (data?: QueryOptions) =>
http.get('/app/ccsq/getBackNode', data),
/** 协同办公/出差申请 查询流程节点 */
get_getFlowNodeUserConfig: (data?: QueryOptions) =>
http.get('/app/ccsq/getFlowNodeUserConfig', data),
@ -381,6 +384,12 @@ export default {
/** 合同系统/申报 退回 */
post_rollback: (data?: BodyOptions) =>
http.post('/app/sbCtrBasePt/rollback', data),
/** 合同系统/申报 发起废除 */
post_repeal: (data?: BodyOptions) =>
http.post('/app/sbCtrBasePt/repeal', data),
/** 合同系统/申报 发起流程 */
post_start: (data?: BodyOptions) =>
http.post('/app/sbCtrBasePt/start', data),
},
contractBaseInfo: {
/** 合同系统/立项 合同立项保存 */
@ -777,4 +786,12 @@ export default {
/** 合同系统/首页待办/已办 待办 */
get_todo: (data?: QueryOptions) => http.get('/app/home/todo', data),
},
sqConsignPt: {
/** 合同系统/签约授权 签约授权保存 */
post_saveSignMultiEntity: (data?: BodyOptions) =>
http.post('/app/sqConsignPt/saveSignMultiEntity', data),
/** 合同系统/签约授权 签约依据查询 */
get_SigningaAuthorizationSerch: (data?: QueryOptions) =>
http.get('/app/sqConsignPt/SigningaAuthorizationSerch', data),
},
};

View File

@ -2,25 +2,23 @@
*
*/
import { preferences } from '@vben/preferences';
import { useAccessStore } from '@vben/stores';
import { createAlova } from 'alova';
import { createServerTokenAuthentication } from 'alova/client';
import fetchAdapter from 'alova/fetch';
import vueHook from 'alova/vue';
import { message } from 'ant-design-vue';
import { merge } from 'lodash-es';
import { useAuthStore } from '#/store';
import { transferResponse } from './transferResponse';
import { type QueryOptions, type BodyOptions } from '../global.d';
import { merge } from 'lodash-es';
import { createAlova } from 'alova';
import fetchAdapter from 'alova/fetch';
import vueHook from 'alova/vue';
import { type BodyOptions, type QueryOptions } from '../global.d';
import { ACCESS_TOKEN_FIELD, getBaseURL } from './config';
import { createServerTokenAuthentication } from 'alova/client';
import { transferResponse } from './transferResponse';
/** 储存过期的token */
let expireTokenCache = [];
const expireTokenCache = [];
/** 服务端 Token 校验 */
const { onAuthRequired, onResponseRefreshToken } =
@ -37,10 +35,10 @@ const { onAuthRequired, onResponseRefreshToken } =
}
const responseClone = response.clone();
let data = await responseClone.json();
const data = await responseClone.json();
// 当服务端返回401时表示token过期
let isExpired = ['401'].includes(data.code);
const isExpired = ['401'].includes(data.code);
if (isExpired) {
console.log('AccessToken已过期', data.code);
expireTokenCache.push(method.config.headers[ACCESS_TOKEN_FIELD]);
@ -93,14 +91,14 @@ export const alovaInstance = createAlova({
// // 统一设置POST的缓存模式
// GET: {
// mode: 'restore',
// expire: 60 * 10 * 1000
// expire: 60 * 10 * 1000,
// },
// POST: {
// mode: 'restore',
// expire: 60 * 10 * 1000
// },
// // 统一设置HEAD请求的缓存模式
// HEAD: 60 * 10 * 1000
// POST: {
// mode: 'restore',
// expire: 60 * 10 * 1000
// },
// // 统一设置HEAD请求的缓存模式
// HEAD: 60 * 10 * 1000
// },
/** 请求拦截器 */
beforeRequest: onAuthRequired((method) => {
@ -136,7 +134,7 @@ export const alovaInstance = createAlova({
/** 响应拦截器 */
responded: onResponseRefreshToken(async (response, method) => {
if (method.meta?.responseType === 'blob') {
let blob = await response.blob();
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
@ -150,14 +148,14 @@ export const alovaInstance = createAlova({
}
a.download = fileName;
// 触发下载
document.body.appendChild(a);
document.body.append(a);
a.click();
// 移除链接
document.body.removeChild(a);
a.remove();
window.URL.revokeObjectURL(url);
return {
blob: blob,
url: url,
blob,
url,
filename: fileName,
};
}
@ -197,14 +195,14 @@ export const alovaInstance = createAlova({
});
class Http {
/**
* swagger路径参数pathParams
* "/api/v1/user/{id}",{ pathParams:{ id :1 },params:{ id:2 } }
* /api/v1/user/1?id=2
*/
private replacePathParams(url: string, pathParams?: any): string {
if (!pathParams) return url;
return url.replace(/{(\w+)}/g, (_, key) => pathParams[key] || `{${key}}`);
private appendParamsToUrl(url: string, params: any): string {
let queryString = '';
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
queryString += `${queryString ? '&' : '?'}${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
}
}
return url + queryString;
}
/**
@ -232,11 +230,24 @@ class Http {
return (options as BodyOptions).data !== undefined;
}
/**
* swagger路径参数pathParams
* "/api/v1/user/{id}",{ pathParams:{ id :1 },params:{ id:2 } }
* /api/v1/user/1?id=2
*/
private replacePathParams(url: string, pathParams?: any): string {
if (!pathParams) return url;
return url.replaceAll(
/\{(\w+)\}/g,
(_, key) => pathParams[key] || `{${key}}`,
);
}
private request(
method: string,
url: string,
data?: BodyOptions | QueryOptions,
) {
): Promise<any> {
let finalUrl = this.replacePathParams(url, data?.pathParams);
const config = this.getConfig(data?.config);
@ -256,7 +267,7 @@ class Http {
});
}
let alovaMethod: 'Get' | 'Post' | 'Delete' | 'Put' = 'Get';
let alovaMethod: 'Delete' | 'Get' | 'Post' | 'Put' = 'Get';
if (method === 'get' || method === 'delete') {
if (method === 'get') {
@ -285,20 +296,18 @@ class Http {
}
}
private appendParamsToUrl(url: string, params: any): string {
let queryString = '';
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
queryString += `${queryString ? '&' : '?'}${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`;
}
}
return url + queryString;
delete(url: string, data?: QueryOptions) {
return this.request('delete', url, data);
}
get(url: string, data?: QueryOptions) {
return this.request('get', url, data);
}
patch(url: string, data?: BodyOptions) {
return this.request('patch', url, data);
}
post(url: string, data?: BodyOptions) {
return this.request('post', url, data);
}
@ -306,14 +315,6 @@ class Http {
put(url: string, data?: BodyOptions) {
return this.request('put', url, data);
}
patch(url: string, data?: BodyOptions) {
return this.request('patch', url, data);
}
delete(url: string, data?: QueryOptions) {
return this.request('delete', url, data);
}
}
export const http = new Http();

View File

@ -1,7 +1,85 @@
<script setup lang="ts">
import type { DictDataType } from '#/utils/dict';
import { computed, onMounted, ref } from 'vue';
import { getDictOpts } from '#/utils/dict';
const props = withDefaults(
defineProps<{
icon?: string;
labelField?: string;
multiple?: boolean;
options: DictDataType[];
selectable?: boolean;
type?: string;
value?: Array<boolean | number | string> | boolean | number | string;
valueField?: string;
}>(),
{
type: '',
options: () => [],
multiple: false,
selectable: false,
labelField: 'label',
valueField: 'value',
value: '',
icon: '',
},
);
const emit = defineEmits(['update:value', 'tag-click']);
const dictData = ref<DictDataType | null>(null);
const selectedValues = ref<Array<number | string>>(
Array.isArray(props.value) ? props.value : [],
);
const dicts = computed(() => {
return props.type ? getDictOpts(props.type) : props.options;
});
const handleClick = (dict: DictDataType) => {
emit('tag-click', dict);
if (props.selectable) {
if (props.multiple) {
const index = selectedValues.value.indexOf(dict.value);
if (index === -1) {
selectedValues.value.push(dict.value);
} else {
selectedValues.value.splice(index, 1);
}
emit('update:value', selectedValues.value);
} else {
emit('update:value', dict.value);
}
}
};
const getTagColor = (dict: DictDataType) => {
return (props.multiple && selectedValues.value.includes(dict.value)) ||
(!props.multiple && props.value == dict.value)
? 'blue'
: '';
};
onMounted(() => {
dictData.value =
(props.options || []).find(
(dict: DictDataType) => dict.value === props.value.toString(),
) || null;
});
</script>
<template>
<template v-if="props.selectable">
<div v-for="(dict, index) in dicts" :key="index" @click="handleClick(dict)"
:class="{ 'cursor-pointer': selectable }">
<div
v-for="(dict, index) in dicts"
:key="index"
:class="{ 'cursor-pointer': selectable }"
@click="handleClick(dict)"
>
<a-tag :color="getTagColor(dict)" :round="!multiple">
{{ dict[labelField] }}
</a-tag>
@ -17,81 +95,12 @@
</template>
</div>
<template v-else>
<!-- <a-tag :color="dictData?.colorType || ''">
{{ (dictData && dictData[labelField]) || value }}
</a-tag> -->
<a-tag :color="dictData?.colorType || ''">
{{ (dictData && dictData[labelField]) || value }}
</a-tag>
</template>
</template>
<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { Tag, Space } from "ant-design-vue";
import { getDictOpts } from "#/utils/dict";
import type { DictDataType } from "#/utils/dict";
const props = withDefaults(
defineProps<{
type?: string;
options: DictDataType[];
value?: string | number | boolean | Array<string | number | boolean>;
icon?: string;
multiple?: boolean;
selectable?: boolean;
labelField?: string;
valueField?: string;
}>(),
{
type: "",
options: () => [],
multiple: false,
selectable: false,
labelField: "label",
valueField: "value",
value: "",
icon: "",
}
);
const emit = defineEmits(["update:value", "tag-click"]);
const dictData = ref<DictDataType | null>(null);
const selectedValues = ref<Array<string | number>>(
Array.isArray(props.value) ? props.value : []
);
const dicts = computed(() => {
return props.type ? getDictOpts(props.type) : props.options;
});
const handleClick = (dict: DictDataType) => {
emit("tag-click", dict);
if (props.selectable) {
if (props.multiple) {
const index = selectedValues.value.indexOf(dict.value);
if (index === -1) {
selectedValues.value.push(dict.value);
} else {
selectedValues.value.splice(index, 1);
}
emit("update:value", selectedValues.value);
} else {
emit("update:value", dict.value);
}
}
};
const getTagColor = (dict: DictDataType) => {
return (props.multiple && selectedValues.value.includes(dict.value)) ||
(!props.multiple && props.value == dict.value)
? "blue"
: "";
};
onMounted(() => {
})
</script>
<style scoped>
.cursor-pointer {
cursor: pointer;

View File

@ -1,11 +1,13 @@
import { h } from 'vue';
import dayjs from 'dayjs';
import { Button, Tag } from 'ant-design-vue';
import { isArray, isString } from 'lodash-es';
import { DictTag } from '#/components/dict-tag';
// import { Icon } from '@/components/Icon';
import { Tooltip } from 'ant-design-vue'
import { Tooltip } from 'ant-design-vue';
import dayjs from 'dayjs';
import { DictTag } from '#/components/dict-tag';
import { getDictOpts } from '#/utils/dict';
export const useRender = {
/**
*
@ -26,7 +28,12 @@ export const useRender = {
* @returns link
*/
renderLink: (url: string, text?: string) => {
if (url) return h(Button, { type: 'link', href: url, target: '_blank' }, () => text || '');
if (url)
return h(
Button,
{ type: 'link', href: url, target: '_blank' },
() => text || '',
);
return '';
},
@ -37,8 +44,7 @@ export const useRender = {
* @returns 1 + 2
*/
renderText: (text: string, val: string) => {
if (text) return `${text} ${val}`;
else return '';
return text ? `${text} ${val}` : '';
},
/**
*
@ -47,19 +53,19 @@ export const useRender = {
*/
renderMultiLineText: (text: string, params?: any) => {
if (text) {
params = params || {}
let classArr: string[] = params.class || [];
params = params || {};
const classArr: string[] = params.class || [];
if (params?.maxLine && params?.maxLine > 0) {
classArr.push('line-clamp-' + params?.maxLine)
classArr.push(`line-clamp-${params?.maxLine}`);
}
return h(
Tooltip,
{ trigger: 'hover' },
{
trigger: () => h('span', { class: classArr.join(' ') }, text),
default: () => text
}
)
default: () => text,
},
);
}
return '';
},
@ -69,9 +75,8 @@ export const useRender = {
* @param color
* @returns
*/
renderTag: (text: string | number, color?: string) => {
if (color) return h(Tag, { color }, () => text);
else return h(Tag, {}, () => text);
renderTag: (text: number | string, color?: string) => {
return color ? h(Tag, { color }, () => text) : h(Tag, {}, () => text);
},
/**
*
@ -97,8 +102,9 @@ export const useRender = {
renderDate: (text: string, format?: string) => {
if (!text) return '';
if (!format) return dayjs(text).format('YYYY-MM-DD HH:mm:ss');
else return dayjs(text).format(format);
return format
? dayjs(text).format(format)
: dayjs(text).format('YYYY-MM-DD HH:mm:ss');
},
/**
*
@ -107,19 +113,20 @@ export const useRender = {
* @returns
*/
renderDict: (text: string, dictType: string, params?: any) => {
debugger;
if (!dictType && !params.options) {
console.warn('请传入字典类型')
console.warn('请传入字典类型');
return '';
}
let dict: any[] = [];
if (dictType) {
dict = getDictOpts(dictType)
dict = getDictOpts(dictType);
}
if (params && params.options) {
dict = params.options;
}
return h(DictTag, { options: dict, value: text, ...params });
}
},
// /**
// * 渲染图标icon
// * @param text icon

View File

@ -27,7 +27,7 @@ const routes: RouteRecordRaw[] = [
hideInTab: true,
icon: 'lucide:area-chart',
title: '立项填报',
activePath: '/contract/approval/edit/:id?',
activePath: '/supervise/edit/:id?',
},
},
{

View File

@ -1,4 +1,3 @@
/** 数据字典工具类 */
import { useDictStore } from '#/store/dict';
@ -15,7 +14,7 @@ const dictStore = useDictStore();
export interface DictDataType {
dictTyp?: string;
label: string;
value: string | number | null;
value: null | number | string;
key?: any;
colorType?: string;
cssClass?: string;
@ -24,7 +23,7 @@ export interface DictDataType {
export interface DictDataOptions {
label?: any;
value?: string | number | null;
value?: null | number | string;
}
export function getDictDatas(dictType: string) {
@ -40,7 +39,10 @@ export function getDictOpts(dictType: string) {
return getDictDatas(dictType);
}
export function getDictOptions(dictType: string, valueType?: 'string' | 'number' | 'boolean'): any[] {
export function getDictOptions(
dictType: string,
valueType?: 'boolean' | 'number' | 'string',
): any[] {
const dictOption: DictDataType[] = [];
valueType ||= 'string';
@ -55,7 +57,7 @@ export function getDictOptions(dictType: string, valueType?: 'string' | 'number'
? `${dict.value}`
: valueType === 'boolean'
? `${dict.value}` === 'true'
: Number.parseInt(`${dict.value}`)
: Number.parseInt(`${dict.value}`),
});
});
}
@ -68,7 +70,11 @@ export function getDictObj(dictType: string, value: any): DictDataType | null {
const dictOptions: DictDataType[] = getDictDatas(dictType);
if (dictOptions) {
if (value) {
return dictOptions.find((dict: DictDataType) => dict.value === value.toString()) || null;
return (
dictOptions.find(
(dict: DictDataType) => dict.value === value.toString(),
) || null
);
}
return null;
} else {
@ -79,9 +85,8 @@ export function getDictObj(dictType: string, value: any): DictDataType | null {
/** 获取字典默认数据 */
export function getDictDefaultObj(dictType: string): DictDataType | null {
const dictOptions: DictDataType[] = getDictDatas(dictType);
if (dictOptions) {
return dictOptions.find((dict: DictDataType) => dict.isDefault == '1') || dictOptions[0];
} else {
return null;
}
return dictOptions
? dictOptions.find((dict: DictDataType) => dict.isDefault == '1') ||
dictOptions[0]
: null;
}

View File

@ -1,70 +1,69 @@
/** 字典枚举 */
export enum DICT_TYPE {
/** 静态枚举-划分标段 */
sectionType = 'section_type',
sectionNum = 'section_num',
commonWhether = 'common_whether',
sysNormalDisable = 'sys_normal_disable',
// 会议类型
meeting_type = 'meeting_type',
meeting_facilities = 'meeting_facilities',
meeting_room = 'meeting_room',
// 订餐
// 主食
canteen_staplefood = 'canteen_staplefood',
/** 就餐方式 */
canteen_dineway = 'canteen_dineway',
// 主食
canteen_staplefood = 'canteen_staplefood',
canteenTimeRange = 'canteen_time_range',
officesupplies_status = 'officesupplies_status',
supervise_task_type = 'supervise_task_type',
supervise_emergency_level = 'supervise_emergency_level',
commonWhether = 'common_whether',
comprehensiveConfig = 'comprehensive_config',
/** 综合管理-项目管理 */
comprehensiveProject = 'comprehensive_project',
/** 综合管理-项目名称管理 */
comprehensiveProjectName = 'comprehensive_project_name',
// 订餐
/** 合同管理-合同审批 */
contractApproval = 'contract_approval',
/** 合同管理-授权类型 */
contractAuthorizationType = 'contract_authorization_type',
/** 合同管理-签约依据类型 */
contractBasisType = 'contract_basis_type',
/** 合同管理-币种单位 */
contractCurrencyUnit = 'contract_currency_unit',
/** 合同管理-资金流向 */
contractFundFlow = 'contract_fund_flow',
/** 合同管理-资金渠道 */
contractFundingSource = 'contract_funding_source',
/** 合同管理-组织形式 */
contractOrganizationForm = 'contract_organization_form',
/** 合同管理-币种单位 */
contractCurrencyUnit = 'contract_currency_unit',
/** 合同管理-合同模块 */
contractModule = 'contract_module',
/** 合同管理-选商方式 */
contractSelectionMethod = 'contract_selection_method',
/** 合同管理-合同审批 */
contractApproval = 'contract_approval',
/** 合同管理-组织形式 */
contractOrganizationForm = 'contract_organization_form',
/** 合同管理-履行状态 */
contractPerformanceStatus = 'contract_performance_status',
/** 合同管理-授权类型 */
contractAuthorizationType = 'contract_authorization_type',
/** 合同管理-商务计价方式 */
contractPriceStyle = 'contract_price_style',
/** 合同管理-项目类型 */
contractProjectType = 'contract_project_type',
/** 合同管理-务计价方式 */
contractPriceStyle = 'contract_price_style',
/** 合同管理-商方式 */
contractSelectionMethod = 'contract_selection_method',
/** 综合管理-项目管理 */
comprehensiveProject = 'comprehensive_project',
meeting_facilities = 'meeting_facilities',
/** 综合管理-项目名称管理 */
comprehensiveProjectName = 'comprehensive_project_name',
meeting_room = 'meeting_room',
comprehensiveConfig = 'comprehensive_config',
// 会议类型
meeting_type = 'meeting_type',
/** 合同管理-签约依据类型 */
contractBasisType = 'contract_basis_type',
officesupplies_status = 'officesupplies_status',
sectionNum = 'section_num',
/** 静态枚举-划分标段 */
sectionType = 'section_type',
supervise_emergency_level = 'supervise_emergency_level',
supervise_task_type = 'supervise_task_type',
sysNormalDisable = 'sys_normal_disable',
}

View File

@ -1,35 +1,33 @@
export default {
/** 变更原因 */
cancel_reson: [
{ label: "未填写原因", value: "0" },
{ label: "用户取消", value: "1" },
{ label: "系统取消", value: "2" },
{ label: "其他原因", value: "3" }
{ label: '未填写原因', value: '0' },
{ label: '用户取消', value: '1' },
{ label: '系统取消', value: '2' },
{ label: '其他原因', value: '3' },
],
/** 划分标段 */
section_type: [
{ label: "是", value: "1" },
{ label: "否", value: "0" }
{ label: '是', value: '1' },
{ label: '否', value: '0' },
],
/** 标段数 */
section_num: [
{ label: "1", value: "1" },
{ label: "2", value: "2" }, ,
{ label: "3", value: "3" },
{ label: "4", value: "4" },
{ label: "5", value: "5" },
{ label: "6", value: "6" },
{ label: "7", value: "7" },
{ label: "8", value: "8" },
{ label: '1', value: '1' },
{ label: '2', value: '2' },
{ label: '3', value: '3' },
{ label: '4', value: '4' },
{ label: '5', value: '5' },
{ label: '6', value: '6' },
{ label: '7', value: '7' },
{ label: '8', value: '8' },
],
/** 常用是否 */
common_whether: [
{ label: "是", value: "1" },
{ label: "否", value: "0" }
]
}
{ label: '是', value: '1' },
{ label: '否', value: '0' },
],
};

View File

@ -0,0 +1,5 @@
export const PrimaryKey = 'guid';
export function getFormSchema(_params: any = {}) {
return [];
}

View File

@ -1,17 +1,12 @@
<script setup lang="ts">
import { nextTick, onMounted, ref } from 'vue';
<script setup lang="tsx">
import { computed, nextTick, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import { MdiUpload } from '@vben/icons';
import { useUserStore } from '@vben/stores';
import {
message,
Modal,
type UploadChangeParam,
type UploadProps,
} from 'ant-design-vue';
import { message, Modal, type UploadChangeParam } from 'ant-design-vue';
import { logger } from 'common-utils';
import dayjs, { Dayjs } from 'dayjs';
@ -35,10 +30,13 @@ const form2Ref = ref();
const isLoading = ref(false);
const currData = ref<any>({});
const nodeInfo = ref<any>({});
function calculateDaysBetweenTimestamps(timestamp1, timestamp2) {
const date1 = new Date(timestamp1);
const date2 = new Date(timestamp2);
const timeDifference = Math.abs(date2 - date1);
const timeDifference = Math.abs(date2.getTime() - date1.getTime());
const daysDifference = timeDifference / (24 * 60 * 60 * 1000);
const roundedDaysDifference = Math.round(daysDifference * 2) / 2;
return roundedDaysDifference;
@ -70,6 +68,10 @@ function handleUpdateFormattedValue() {
}
}
const readOnly = computed(() => {
return id.value && currData.value.status === 'edit';
});
const disabledDate = (current: Dayjs) => {
// Can not select days before today and today
const form = formRef.value.form;
@ -92,10 +94,18 @@ const formBinding = ref({
// disabledDate: (current) => current && current < dayjs().endOf("day"),
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm',
onChange: (e) => {
onChange: () => {
handleUpdateFormattedValue();
},
},
conditionalRender: {
match(_context) {
return readOnly.value;
},
render({ form }) {
return <span>{form.starttime}</span>;
},
},
rules: [{ required: true, message: '请选择出发时间' }],
},
endtime: {
@ -107,12 +117,20 @@ const formBinding = ref({
allowClear: false,
showTime: { format: 'HH:mm' },
disabledDate,
onChange: (e) => {
onChange: () => {
handleUpdateFormattedValue();
},
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm',
},
conditionalRender: {
match(_context) {
return readOnly.value;
},
render({ form }) {
return <span>{form.endtime}</span>;
},
},
rules: [{ required: true, message: '请选择结束时间' }],
},
days: {
@ -123,6 +141,16 @@ const formBinding = ref({
name: 'a-input-number',
vModel: 'value',
min: 0,
addonAfter: '天',
placeholder: '请输入出差天数',
},
conditionalRender: {
match(_context) {
return readOnly.value;
},
render({ form }) {
return <span>{form.days} </span>;
},
},
},
place: {
@ -134,6 +162,14 @@ const formBinding = ref({
vModel: 'value',
props: {},
},
conditionalRender: {
match(_context) {
return readOnly.value;
},
render({ form }) {
return <span>{form.place}</span>;
},
},
rules: [{ required: true, message: '请输入出差地点' }],
},
reasons: {
@ -145,6 +181,14 @@ const formBinding = ref({
vModel: 'value',
props: {},
},
conditionalRender: {
match(_context) {
return readOnly.value;
},
render({ form }) {
return <span>{form.reasons}</span>;
},
},
},
phone: {
title: '联系方式',
@ -155,6 +199,14 @@ const formBinding = ref({
vModel: 'value',
props: {},
},
conditionalRender: {
match(_context) {
return readOnly.value;
},
render({ form }) {
return <span>{form.phone}</span>;
},
},
},
remarks: {
title: '备注',
@ -165,6 +217,14 @@ const formBinding = ref({
vModel: 'value',
props: {},
},
conditionalRender: {
match(_context) {
return readOnly.value;
},
render({ form }) {
return <span>{form.remarks}</span>;
},
},
},
},
});
@ -181,10 +241,6 @@ const form2Binding = ref({
},
});
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
return false;
};
function handleBack() {
Modal.confirm({
title: '提示',
@ -208,6 +264,21 @@ const handleChange = (info: UploadChangeParam) => {
});
};
function handleDelete() {
Modal.confirm({
title: '提示',
content: '是否确认删除该条记录?',
okType: 'danger',
onOk: async () => {
await Apis.ccsq.post_deletes({
params: { ids: id.value },
});
message.success('删除成功');
back();
},
});
}
const chooseUserIds = ref([]);
async function handleChooseUserConfirm(e) {
@ -215,19 +286,23 @@ async function handleChooseUserConfirm(e) {
isLoading.value = true;
try {
//
// const nodeInfo = await getNodeInfo(nodeId);
chooseUserIds.value = e.map((item) => item.ACCOUNT_ID);
if (!nodeInfo.value.variableName) {
throw new Error('出差审批节点异常,请稍候再试');
}
await Apis.ccsq.post_startWorkFlow({
data: {
guid: id.value,
assigneeList: chooseUserIds.value,
variables: {
[nodeInfo.value.variableName]: chooseUserIds.value,
},
},
});
message.success('提交成功');
back();
} catch (error) {
logger.error('合同立项提交失败', error);
logger.error('出差提交失败', error);
message.error('提交失败,请稍候再试');
} finally {
isLoading.value = false;
@ -238,27 +313,26 @@ async function handleSave() {
isLoading.value = true;
try {
await formRef.value.submit();
const form = formRef.value.form;
// await formRef.value.submit()
console.log(formRef.value);
const userStore = useUserStore();
form.applyName = userStore.userInfo.gwmc;
form.applyId = userStore.userInfo.userId;
form.applyName = (userStore.userInfo as any).gwmc;
form.applyId = userStore.userInfo!.userId;
form.applyTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
console.log(form2Ref.value.form);
const fileList = form2Ref.value.form.fileList;
let files: any = [];
if (fileList && fileList.length > 0) {
files = await fileUploader.upload(fileList, { source: 'erp' });
}
console.log(files);
if (files) {
form.fileUuid = (files.map((item) => item.fileUuid) || []).join(',');
}
const data = await Apis.ccsq.post_save({ data: form });
id.value = data.guid;
currData.value.status = '已创建';
message.success('保存成功');
Modal.confirm({
title: '提示',
@ -272,16 +346,25 @@ async function handleSave() {
});
} catch (error) {
message.error('提交失败,请稍候再试');
console.log(error);
logger.error('出差申请提交失败', error);
} finally {
isLoading.value = false;
}
}
function handleSubmit() {
async function handleSubmit() {
await formRef.value.submit();
// isLoading.value = true
//
let tempNodeInfo = await Apis.ccsq.get_getFlowNodeUserConfig({
params: { guid: id.value },
});
nodeInfo.value = tempNodeInfo = tempNodeInfo.rows[0];
chooseUserModalApi.setData({
title: '选择审批人',
title: `选择${tempNodeInfo.name}(${tempNodeInfo.selectMode})`,
limitMultipleNum: tempNodeInfo.selectMode === '多选' ? 10 : 1,
userIds: chooseUserIds.value || [],
});
chooseUserModalApi.open();
@ -289,27 +372,21 @@ function handleSubmit() {
onMounted(async () => {
isLoading.value = true;
console.log(id.value);
try {
if (id.value) {
let data = await Apis.ccsq.get_page({ params: { guid: id.value } });
data = data.rows[0];
data = data.rows[0] || {};
currData.value = data;
formRef.value.setFormData(data);
if (data.fileUuid) {
const files = await fileUploader.select(data.fileUuid);
console.log(files);
form2Ref.value.setFormData(
{
fileList: files,
},
50,
);
form2Ref.value.setFormData({ fileList: files });
}
}
} catch (error) {
console.log(error);
logger.error('当前出差申请不存在', error);
Modal.error({
title: '提示',
content: '当前出差申请不存在',
@ -337,10 +414,28 @@ onMounted(async () => {
<a-spin :spinning="isLoading">
<a-space>
<vben-button variant="primary" @click="handleSave()">保存</vben-button>
<vben-button variant="primary" @click="handleSubmit()">
<vben-button
v-if="!id || ['已创建', '退回'].includes(currData.status)"
variant="primary"
@click="handleSave()"
>
保存
</vben-button>
<vben-button
v-if="!id || ['已创建', '退回'].includes(currData.status)"
:disabled="!id"
variant="primary"
@click="handleSubmit()"
>
提交
</vben-button>
<vben-button
v-if="!isLoading && id && !['待审核'].includes(currData.status)"
variant="destructive"
@click="handleDelete()"
>
删除
</vben-button>
<vben-button variant="secondary" @click="handleBack()">
返回
</vben-button>
@ -356,13 +451,13 @@ onMounted(async () => {
<template #form_fileList="scope">
<a-upload
v-model:file-list="scope.form.fileList"
:before-upload="beforeUpload"
:before-upload="() => false"
:max-count="3"
accept=".pdf"
name="file"
@change="handleChange"
>
<vben-button variant="secondary">
<vben-button v-if="!readOnly" variant="secondary">
<MdiUpload />
点击上传
</vben-button>

View File

@ -1,12 +1,20 @@
import type { VxeGridPropTypes } from 'vxe-table';
import { useRender } from '#/hooks/useRender';
import dayjs from 'dayjs';
import { useRender } from '#/hooks/useRender';
export const PrimaryKey = 'guid';
export function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: 'radio', width: 40, slots: { radio: 'radio_cell' }, align: 'center', fixed: 'left' },
{
type: 'radio',
width: 40,
slots: { radio: 'radio_cell' },
align: 'center',
fixed: 'left',
},
{
field: 'applyTime',
title: '申请日期',
@ -14,20 +22,32 @@ export function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
fixed: 'left',
},
{
field: 'status', title: '记录状态', width: 120, slots: {
field: 'status',
title: '记录状态',
width: 120,
slots: {
default: ({ row }) => {
const statusMap: any = {
'已创建': 'default',
'待审核': 'warning',
'已完成': 'success'
: 'default',
: 'warning',
: 'success',
退: 'error',
};
return useRender.renderTag(row.status, statusMap[row.status] || 'default');
}
}
return useRender.renderTag(
row.status,
statusMap[row.status] || 'default',
);
},
},
},
{
field: 'base',
title: '出差时间',
width: 200,
slots: { default: 'baseSlot' },
},
{ field: 'base', title: '出差时间', width: 200, slots: { default: 'baseSlot' } },
{ field: 'days', title: '出差天数', width: 80 },
{ field: 'place', title: '出差地点', width: 200, },
{ field: 'place', title: '出差地点', width: 200 },
{ field: 'reasons', title: '出差事项', width: 200 },
{ field: 'phone', title: '联系方式', width: 200 },
// { field: 'place', title: '出差地点', width: 200, slots: { default: 'ddy' } },

View File

@ -1,46 +1,27 @@
<script setup lang="ts">
import { defineComponent, ref, computed, reactive, onMounted } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { useVxeTable } from '#/hooks/vxeTable';
import {
type CreateCrudOptionsProps,
useColumns,
useFormWrapper,
useFs,
utils,
} from '@fast-crud/fast-crud';
import { computed, onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import {
MdiAdd,
MdiUpdate,
MdiDelete,
MdiExport,
MdiRadioUnchecked,
MdiRadioChecked,
MdiRadioUnchecked,
MdiUpdate,
} from '@vben/icons';
import { getColumns } from './crud.tsx';
import { dict } from '@fast-crud/fast-crud';
import { getMonthStartAndEnd } from '#/utils/time';
import Apis from '#/api';
import dayjs from 'dayjs';
import { message } from 'ant-design-vue';
import { Modal } from 'ant-design-vue';
import { useRouter } from 'vue-router';
import { message, Modal } from 'ant-design-vue';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { getColumns } from './crud.tsx';
const router = useRouter();
const checkedValue = ref('all');
const exportSearchParams = ref<any>({
daterange: getMonthStartAndEnd(),
});
const radioStyle = reactive({
display: 'flex',
height: '30px',
lineHeight: '30px',
});
const searchRef = ref();
let isConfirmLoading = ref(false);
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
@ -77,22 +58,23 @@ function handleAdd() {
}
function handleUpdate(row) {
router.push('/bussiness-trip/edit/' + row['guid']);
router.push(`/bussiness-trip/edit/${row.guid}`);
}
function handleDelete(row) {
if (['待审核'].includes(row.status)) {
message.warning('该记录已提交,不能删除');
return;
}
Modal.confirm({
title: '提示',
content: '是否确认删除该条记录?',
okType: 'danger',
onOk: async () => {
await Apis.ccsq.post_deletes({ params: { ids: row['guid'] } });
await Apis.ccsq.post_deletes({ params: { ids: row.guid } });
message.success('删除成功');
triggerProxy('reload');
},
onCancel() {
console.log('Cancel');
},
});
}
@ -107,13 +89,13 @@ function handleExport() {
}
/** 选中数据 */
const selectRow: ant = computed(() => {
const selectRow: any = computed(() => {
return xGridRef.value?.getRadioRecord() || null;
});
/** 单选框选中事件 */
const setSelectRow = (row: ant) => {
if (selectRow.value && selectRow.value['guid'] === row['guid']) {
const setSelectRow = (row: any) => {
if (selectRow.value && selectRow.value.guid === row.guid) {
xGridRef.value?.clearRadioRow();
} else {
xGridRef.value?.setRadioRow(row);
@ -139,29 +121,41 @@ const searchForm = ref({
name: 'a-date-picker',
vModel: 'value',
allowClear: true,
props: {
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
},
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
},
autoSearchTrigger: 'enter',
show: true,
},
endDate: {
title: '截止月份',
title: '截止日期',
key: 'endDate',
component: {
name: 'a-date-picker',
vModel: 'value',
allowClear: true,
props: {
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
},
format: 'YYYY-MM-DD',
valueFormat: 'YYYY-MM-DD',
},
autoSearchTrigger: 'enter',
show: true,
},
status: {
title: '记录状态',
key: 'status',
component: {
name: 'a-select',
vModel: 'value',
allowClear: true,
class: 'min-w-[180px]',
options: [
{ label: '已创建', value: '已创建' },
{ label: '待审核', value: '待审核' },
{ label: '退回', value: '退回' },
],
},
show: true,
},
},
onSearch(_context: any) {
triggerProxy('reload');
@ -171,31 +165,14 @@ const searchForm = ref({
</script>
<template>
<Page contentClass="h-full flex flex-col">
<SubmitModal
class="w-[600px]"
title="选择送审人"
:loading="isConfirmLoading"
:confirmLoading="isConfirmLoading"
>
<a-radio-group v-model:value="checkedValue">
<a-radio :style="radioStyle" value="all">导出全部</a-radio>
<a-radio :style="radioStyle" value="daterange">
导出时间范围
<div class="ml-2">
<a-range-picker
v-model:value="exportSearchParams.daterange"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</div>
</a-radio>
</a-radio-group>
</SubmitModal>
<fs-search ref="searchRef" v-bind="searchForm"> </fs-search>
<Page content-class="h-full flex flex-col">
<fs-search ref="searchRef" v-bind="searchForm" />
<div class="min-h-300px flex-1">
<vxe-grid ref="xGridRef" v-bind="gridOptions" @cell-click="handleCellClick">
<vxe-grid
ref="xGridRef"
v-bind="gridOptions"
@cell-click="handleCellClick"
>
<template #toolbar_buttons>
<a-space>
<vben-button variant="primary" @click="handleAdd()">
@ -203,8 +180,8 @@ const searchForm = ref({
新增
</vben-button>
<vben-button
:disabled="!selectRow || !selectRow.guid"
variant="warning"
:disabled="!selectRow || !selectRow['guid']"
@click="handleUpdate(selectRow)"
>
<MdiUpdate class="mr-0.5 text-lg" />
@ -215,8 +192,8 @@ const searchForm = ref({
导出
</vben-button>
<vben-button
:disabled="!selectRow || !selectRow.guid"
variant="destructive"
:disabled="!selectRow || !selectRow['guid']"
@click="handleDelete(selectRow)"
>
<MdiDelete class="mr-0.5 text-lg" />

View File

@ -1,51 +1,132 @@
import type { VxeGridPropTypes } from 'vxe-table';
import { useRender } from '#/hooks/useRender';
import dayjs from 'dayjs';
export const PrimaryKey = 'guid';
export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
let columns: VxeGridPropTypes.Columns = [
const columns: VxeGridPropTypes.Columns = [
{ type: 'seq', width: 50, align: 'center', fixed: 'left' },
{
field: 'applyTime', title: '申请日期', width: 130,
field: 'auditTime',
title: '申请日期',
width: 130,
},
{
field: 'status', title: '记录状态', width: 120, slots: {
field: 'status',
title: '记录状态',
width: 120,
slots: {
default: ({ row }) => {
const statusMap: any = {
'已创建': 'default',
'待审核': 'warning',
'已完成': 'success'
: 'default',
: 'warning',
: 'success',
退: 'error',
};
return useRender.renderTag(row.status, statusMap[row.status] || 'default');
}
}
return useRender.renderTag(
row.status,
statusMap[row.status] || 'default',
);
},
},
},
{
field: 'applyName', title: '出差人员', minWidth: 200,
field: 'taskName',
title: '任务名称',
width: 160,
},
{
field: 'applyName',
title: '出差人员',
minWidth: 200,
},
{
field: 'base',
title: '出差时间',
width: 200,
slots: { default: 'baseSlot' },
},
{ field: 'base', title: '出差时间', width: 200, slots: { default: 'baseSlot' } },
{ field: 'days', title: '出差天数', width: 80 },
{ field: 'place', title: '出差地点', width: 200, },
{ field: 'place', title: '出差地点', width: 200 },
{ field: 'reasons', title: '出差事项', width: 200 },
{ field: 'phone', title: '联系方式', width: 200 },
{ field: 'remarks', title: '备注', minWidth: 200 },
]
];
if (params.todoType === 'todo') {
columns.push({
field: "operate",
title: "操作",
field: 'operate',
title: '操作',
width: 110,
fixed: "right",
slots: { default: "operate" },
})
fixed: 'right',
slots: { default: 'operate' },
});
}
return columns
return columns;
}
export function getDoneColumns(params: any = {}): VxeGridPropTypes.Columns {
const columns: VxeGridPropTypes.Columns = [
{ type: 'seq', width: 50, align: 'center', fixed: 'left' },
{
field: 'applyTime',
title: '申请日期',
width: 130,
},
{
field: 'status',
title: '记录状态',
width: 120,
slots: {
default: ({ row }) => {
const statusMap: any = {
: 'default',
: 'warning',
: 'success',
退: 'error',
};
return useRender.renderTag(
row.status,
statusMap[row.status] || 'default',
);
},
},
},
{
field: 'taskName',
title: '任务名称',
width: 160,
},
{
field: 'applyName',
title: '出差人员',
minWidth: 200,
},
{
field: 'base',
title: '出差时间',
width: 200,
slots: { default: 'baseSlot' },
},
{ field: 'days', title: '出差天数', width: 80 },
{ field: 'place', title: '出差地点', width: 200 },
{ field: 'reasons', title: '出差事项', width: 200 },
{ field: 'phone', title: '联系方式', width: 200 },
{ field: 'remarks', title: '备注', minWidth: 200 },
{
field: 'operate',
title: '操作',
width: 80,
fixed: 'right',
slots: { default: 'operate' },
},
];
return columns;
}
export function getFormSchema(_params: any = {}) {
return []
return [];
}

View File

@ -1,17 +1,20 @@
<script setup lang="ts">
import { defineComponent, ref, reactive, onMounted, nextTick } from 'vue';
import { FsCrud } from '@fast-crud/fast-crud';
import { type VxeGridProps } from 'vxe-table';
import { Page, useVbenModal } from '@vben/common-ui';
import { useVxeTable } from '#/hooks/vxeTable';
import { onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { logger } from 'common-utils';
import { MdiAdd, MdiUpdate, MdiDelete } from '@vben/icons';
import { dict } from '@fast-crud/fast-crud';
import { getColumns } from './crud';
import Apis from '#/api';
import { message, Modal } from 'ant-design-vue';
import { useVxeTable } from '#/hooks/vxeTable';
import chooseUserModal from '#/views/system/user/choose-user-modal.vue';
import { getColumns, getDoneColumns } from './crud';
const router = useRouter();
const [ChooseUserModal, chooseUserModalApi] = useVbenModal({
connectedComponent: chooseUserModal,
});
@ -22,9 +25,21 @@ const { xGridRef: xGrid2Ref, triggerProxy: triggerProxy2 } = useVxeTable({
ref: 'xGrid2Ref',
});
let selectRow = ref({});
const selectRow = ref<any>({});
const treeData = ref([]);
const nodeInfo = ref<any>({});
const chooseUserIds = ref([]);
const isLoading = ref(false);
const title = ref('1');
const form2Ref = ref();
const form2Binding = ref({
col: { span: 24 },
columns: {},
});
/** Hooks - 表格 */
const gridOptions = reactive(
@ -40,7 +55,6 @@ const gridOptions = reactive(
params: {
pageNum: page.currentPage,
pageSize: page.pageSize,
...searchParams,
},
});
},
@ -60,7 +74,7 @@ const grid2Options = reactive(
gridProps({
height: '100%',
size: 'mini',
columns: getColumns({ todoType: 'done' }),
columns: getDoneColumns({ todoType: 'done' }),
proxyConfig: {
autoLoad: true,
ajax: {
@ -69,7 +83,6 @@ const grid2Options = reactive(
params: {
pageNum: page.currentPage,
pageSize: page.pageSize,
...searchParams,
},
});
},
@ -84,50 +97,13 @@ const grid2Options = reactive(
}),
);
const assigneeList = ref([]);
onMounted(() => {});
let searchParams = reactive({});
const searchForm = ref({
columns: {
name: {
component: {
name: 'a-input',
vModel: 'value',
allowClear: true,
props: {
type: 'text',
showWordLimit: true,
},
},
title: '字典标签',
key: 'type',
autoSearchTrigger: 'enter',
show: true,
},
age: {
component: {
name: 'a-input',
vModel: 'value',
allowClear: true,
},
title: '字典键值',
key: 'value',
autoSearchTrigger: 'enter',
show: true,
},
},
onSearch(context: any) {
searchParams = context.form;
triggerProxy('reload');
},
onReset(context: any) {
searchParams = context.form;
},
});
const isConfirmLoading = ref(false);
let isConfirmLoading = ref(false);
const [TemporaryModalApi, temporaryModalApi] = useVbenModal({
const [TemporaryModal, temporaryModalApi] = useVbenModal({
onOpenChange(isOpen: boolean) {
if (isOpen) {
isConfirmLoading.value = false;
@ -135,24 +111,58 @@ const [TemporaryModalApi, temporaryModalApi] = useVbenModal({
},
async onConfirm() {
// await
handleAudit(selectRow.value, 'rejectConfirm');
},
});
async function handleAudit(row, type) {
selectRow.value = row;
//
let tempNodeInfo = await Apis.ccsq.post_getNextNodeUserConfig({
params: { taskId: row.taskId },
});
nodeInfo.value = tempNodeInfo = tempNodeInfo.rows[0];
chooseUserModalApi.setData({
title: `选择${tempNodeInfo.name}(${tempNodeInfo.selectMode})`,
limitMultipleNum: tempNodeInfo.selectMode === '多选' ? 10 : 1,
userIds: chooseUserIds.value || [],
});
if (type === 'access') {
chooseUserModalApi.setData({
title: '选择审批人',
});
chooseUserModalApi.open();
return;
}
if (type === 'accessConfirm') {
try {
await Apis.ccsq.post_submit({
data: {
guid: selectRow.value.guid,
variables: {
[nodeInfo.value.variableName]: assigneeList.value,
},
comment: '通过',
},
});
message.success('提交成功');
triggerProxy('reload');
triggerProxy2('reload');
} catch (error) {
logger.error('出差审批提交失败', error);
message.error('提交失败,请稍候再试');
} finally {
isLoading.value = false;
}
return;
}
if (type === 'reject') {
await Apis.ccsq.get_getBackNode({
params: { taskId: row.taskId },
});
// let tempBackNodeInfo = await Apis.ccsq.get_getBackNode({
// params: { taskId: row.taskId },
// });
// tempBackNodeInfo = tempBackNodeInfo.rows[0]
form2Binding.value.columns = {};
@ -161,63 +171,57 @@ async function handleAudit(row, type) {
comment: {
title: '',
key: 'comment',
col: { span: 10 },
col: { span: 24 },
colon: false,
component: {
name: 'a-time-picker',
name: 'a-textarea',
vModel: 'value',
format: 'HH:mm',
valueFormat: 'HH:mm',
autoSize: { minRows: 4, maxRows: 6 },
placeholder: '请输入',
},
},
};
temporaryModalApi.open();
nextTick(() => {
form2Ref.value.setFormData(submitForm.value);
isConfirmLoading.value = true;
}
if (type === 'rejectConfirm') {
await Apis.ccsq.post_rollback({
params: {
...form2Ref.value.form,
guid: selectRow.value.guid,
backNodeId: 'userTask_first_node',
},
});
temporaryModalApi.close();
isConfirmLoading.value = false;
triggerProxy('reload');
triggerProxy2('reload');
}
}
async function handleChooseUserConfirm(e) {
console.log(e);
chooseUserModalApi.close();
try {
await Apis.ccsq.post_submit({
data: {
guid: selectRow.value.guid,
assigneeList: e.map((item) => item.ACCOUNT_ID),
comment: '通过',
},
});
message.success('提交成功');
} catch (error) {
console.log(error);
message.error('提交失败,请稍候再试');
} finally {
}
assigneeList.value = e.map((item) => item.ACCOUNT_ID);
handleAudit(selectRow.value, 'accessConfirm');
}
const title = ref('1');
const form2Ref = ref();
const form2Binding = ref({
col: { span: 24 },
columns: {},
});
function toPage(row) {
router.push(`/bussiness-trip/edit/${row.guid || row.businessKey}`);
}
</script>
<template>
<Page contentClass="h-full flex flex-col">
<Page content-class="h-full flex flex-col">
<TemporaryModal
class="w-[400px]"
:title="title"
contentClass="min-h-0"
:confirm-loading="isConfirmLoading"
:loading="isConfirmLoading"
:confirmLoading="isConfirmLoading"
:title="title"
class="w-[600px]"
content-class="min-h-0"
>
<fs-form ref="form2Ref" v-bind="form2Binding"> </fs-form>
<fs-form ref="form2Ref" v-bind="form2Binding" />
</TemporaryModal>
<ChooseUserModal
@ -226,12 +230,12 @@ const form2Binding = ref({
@confirm="handleChooseUserConfirm"
/>
<a-space direction="vertical" size="small" class="flex h-full flex-col">
<a-space class="flex h-full flex-col" direction="vertical" size="small">
<a-card
title="待办"
size="small"
:body-style="{ flex: '1' }"
class="flex h-full flex-col"
:bodyStyle="{ flex: '1' }"
size="small"
title="待办"
>
<vxe-grid ref="xGridRef" v-bind="gridOptions" class="h-full">
<template #toolbar_buttons> </template>
@ -244,16 +248,16 @@ const form2Binding = ref({
<template #operate="{ row }">
<a-space>
<a-button
type="text"
size="small"
type="text"
@click="handleAudit(row, 'access')"
>
通过
</a-button>
<a-button
type="text"
danger
size="small"
type="text"
@click="handleAudit(row, 'reject')"
>
拒绝
@ -263,10 +267,10 @@ const form2Binding = ref({
</vxe-grid>
</a-card>
<a-card
title="已办"
size="small"
:body-style="{ flex: '1' }"
class="flex h-full flex-col"
:bodyStyle="{ flex: '1' }"
size="small"
title="已办"
>
<vxe-grid ref="xGrid2Ref" v-bind="grid2Options">
<template #toolbar_buttons> </template>
@ -274,6 +278,14 @@ const form2Binding = ref({
<p>开始时间{{ row.starttime }}</p>
<p>结束时间{{ row.endtime }}</p>
</template>
<template #operate="{ row }">
<a-space>
<a-button size="small" type="text" @click="toPage(row)">
查看
</a-button>
</a-space>
</template>
</vxe-grid>
</a-card>
</a-space>

View File

@ -1,78 +1,80 @@
import type { VxeGridPropTypes } from 'vxe-table';
import { useRender } from '#/hooks/useRender';
import dayjs from 'dayjs';
import { DICT_TYPE, getDictObj, getDictOptions } from '#/utils/dict';
import { dict } from '@fast-crud/fast-crud';
import { unitComponentProps } from '#/common/unit';
import { useRender } from '#/hooks/useRender';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
export const PrimaryKey = 'guid';
export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
return [
{
type: "radio",
type: 'radio',
width: 40,
slots: { radio: "radio_cell" },
align: "center",
fixed: "left",
slots: { radio: 'radio_cell' },
align: 'center',
fixed: 'left',
},
{ field: "applyName", title: "申请人", width: 120 },
{ field: "officeName", title: "申请物品", minWidth: 150 },
{ field: 'applyName', title: '申请人', width: 120 },
{ field: 'officeName', title: '申请物品', minWidth: 150 },
{
field: "model",
title: "物品型号",
field: 'model',
title: '物品型号',
minWidth: 150,
},
{
field: "count",
title: "申请数量",
field: 'count',
title: '申请数量',
minWidth: 80,
slots: {
default: ({ row }) => {
return row.count + "个";
return `${row.count}`;
},
},
},
{ field: "remarks", title: "申请原因", width: 200 },
{ field: 'remarks', title: '申请原因', width: 200 },
{
field: "status",
title: "申请状态",
field: 'status',
title: '申请状态',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDict(row.status, DICT_TYPE.officesupplies_status, {
labelField: "extend1",
});
return useRender.renderDict(
row.status,
DICT_TYPE.officesupplies_status,
{
labelField: 'extend1',
},
);
},
},
},
{
field: "companyRealCount",
title: "实际发放量",
field: 'companyRealCount',
title: '实际发放量',
width: 100,
showOverflow: true,
slots: {
default: ({ row }) => {
return useRender.renderText(row.companyRealCount, "个");
return useRender.renderText(row.companyRealCount, '个');
},
},
},
{ field: "time", title: "申请时间", width: 150 },
]
{ field: 'time', title: '申请时间', width: 150 },
];
}
export function getFormSchema(params: any = {}) {
const { chooseUserModalApi } = params;
const { chooseUserModalApi } = params
let data = getDictOptions(DICT_TYPE.officesupplies_status);
const data = getDictOptions(DICT_TYPE.officesupplies_status);
data.forEach((item) => {
item.label = item.extend1;
});
return {
initialForm: {
},
initialForm: {},
columns: {
status: {
title: '申请状态',
@ -83,8 +85,8 @@ export function getFormSchema(params: any = {}) {
class: 'min-w-[180px]',
allowClear: true,
dict: dict({
data: data
})
data,
}),
},
autoSearchTrigger: 'enter',
show: true,
@ -126,6 +128,5 @@ export function getFormSchema(params: any = {}) {
show: true,
},
},
}
};
}

View File

@ -1,78 +1,173 @@
import type { VxeGridPropTypes } from 'vxe-table';
import { useRender } from '#/hooks/useRender';
import dayjs from 'dayjs';
import { DICT_TYPE, getDictObj, getDictOptions } from '#/utils/dict';
import { dict } from '@fast-crud/fast-crud';
import { unitComponentProps } from '#/common/unit';
import { useRender } from '#/hooks/useRender';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
export const PrimaryKey = 'guid';
export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: 'seq', width: 50, align: 'center', fixed: 'left' },
{
type: "radio",
width: 40,
slots: { radio: "radio_cell" },
align: "center",
fixed: "left",
},
{ field: "applyName", title: "申请人", width: 120 },
{ field: "officeName", title: "申请物品", minWidth: 150 },
{
field: "model",
title: "物品型号",
minWidth: 150,
},
{
field: "count",
title: "申请数量",
minWidth: 80,
slots: {
default: ({ row }) => {
return row.count + "个";
},
},
},
{ field: "remarks", title: "申请原因", width: 200 },
{
field: "status",
title: "申请状态",
field: 'status',
title: '申请状态',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDict(row.status, DICT_TYPE.officesupplies_status, {
labelField: "extend1",
});
return useRender.renderDict(
row.status,
DICT_TYPE.officesupplies_status,
{
labelField: 'extend1',
},
);
},
},
},
{ field: 'applyName', title: '申请人', width: 120 },
{ field: 'officeName', title: '申请物品', minWidth: 150 },
{
field: 'model',
title: '物品型号',
minWidth: 150,
},
{
field: 'count',
title: '申请数量',
minWidth: 80,
slots: {
default: ({ row }) => {
return `${row.count}`;
},
},
},
{ field: 'remarks', title: '申请原因', width: 200 },
{
field: 'status',
title: '申请状态',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDict(
row.status,
DICT_TYPE.officesupplies_status,
{
labelField: 'extend1',
},
);
},
},
},
{
field: "companyRealCount",
title: "实际发放量",
field: 'companyRealCount',
title: '实际发放量',
width: 100,
showOverflow: true,
slots: {
default: ({ row }) => {
return useRender.renderText(row.companyRealCount, "个");
return useRender.renderText(row.companyRealCount, '个');
},
},
},
{ field: "time", title: "申请时间", width: 150 },
]
{ field: 'remarks', title: '申请原因', minWidth: 150 },
{ field: 'time', title: '申请时间', width: 150 },
{
field: 'num',
title: '审核数量',
width: 200,
fixed: 'right',
slots: { default: 'edit_num' },
},
{
field: 'operate',
title: '操作',
width: 110,
fixed: 'right',
slots: { default: 'operate' },
},
];
}
export function getDoneColumns(params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: 'seq', width: 50, align: 'center', fixed: 'left' },
{
field: 'status',
title: '申请状态',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDict(
row.status,
DICT_TYPE.officesupplies_status,
{
labelField: 'extend1',
},
);
},
},
},
{ field: 'applyName', title: '申请人', width: 120 },
{ field: 'officeName', title: '申请物品', minWidth: 150 },
{
field: 'model',
title: '物品型号',
minWidth: 150,
},
{
field: 'count',
title: '申请数量',
minWidth: 80,
slots: {
default: ({ row }) => {
return `${row.count}`;
},
},
},
{ field: 'remarks', title: '申请原因', width: 200 },
{
field: 'status',
title: '申请状态',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDict(
row.status,
DICT_TYPE.officesupplies_status,
{
labelField: 'extend1',
},
);
},
},
},
{
field: 'companyRealCount',
title: '实际发放量',
width: 100,
showOverflow: true,
slots: {
default: ({ row }) => {
return useRender.renderText(row.companyRealCount, '个');
},
},
},
{ field: 'time', title: '申请时间', width: 150 },
];
}
export function getFormSchema(params: any = {}) {
const { chooseUserModalApi } = params;
const { chooseUserModalApi } = params
let data = getDictOptions(DICT_TYPE.officesupplies_status);
const data = getDictOptions(DICT_TYPE.officesupplies_status);
data.forEach((item) => {
item.label = item.extend1;
});
return {
initialForm: {
},
initialForm: {},
columns: {
status: {
title: '申请状态',
@ -83,8 +178,8 @@ export function getFormSchema(params: any = {}) {
class: 'min-w-[180px]',
allowClear: true,
dict: dict({
data: data
})
data,
}),
},
autoSearchTrigger: 'enter',
show: true,
@ -126,6 +221,5 @@ export function getFormSchema(params: any = {}) {
show: true,
},
},
}
};
}

View File

@ -1,12 +1,251 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { getColumns, getDoneColumns } from './crud.tsx';
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const { xGridRef: xGrid2Ref, triggerProxy: triggerProxy2 } = useVxeTable({
ref: 'xGrid2Ref',
});
const title = ref('1');
const form2Ref = ref();
const form2Binding = ref({
col: { span: 24 },
columns: {},
});
const isConfirmLoading = ref(false);
const selectRow = ref<any>({});
const [TemporaryModal, temporaryModalApi] = useVbenModal({
onOpenChange(isOpen: boolean) {
if (isOpen) {
isConfirmLoading.value = false;
}
},
async onConfirm() {
// await
handleAudit(selectRow.value, 'rejectConfirm');
},
});
/** Hooks - 表格 */
const gridOptions = reactive(
gridProps({
height: '100%',
columns: getColumns(),
proxyConfig: {
autoLoad: true,
ajax: {
query: ({ page }) => {
return Apis.officeSuppliesApply.get_page({
params: { pageNum: page.currentPage, pageSize: page.pageSize },
});
},
},
},
pagerConfig: {
enabled: true,
},
toolbarConfig: {
enabled: false,
},
}),
);
/** Hooks - 表格 */
const grid2Options = reactive(
gridProps({
height: '100%',
columns: getDoneColumns(),
proxyConfig: {
autoLoad: true,
ajax: {
query: ({ page }) => {
return Apis.officeSuppliesApply.get_page({
params: {
pageNum: page.currentPage,
pageSize: page.pageSize,
status: 'officeSuppliesAudit',
},
});
},
},
},
pagerConfig: {
enabled: true,
},
toolbarConfig: {
enabled: false,
},
}),
);
async function handleAudit(
row: any,
type: 'access' | 'reject' | 'rejectConfirm',
) {
selectRow.value = row;
let form = {
guid: row.guid,
status: '', // departmentAudit
unitCount: null,
companyApprovalCount: null,
companyRealCount: null,
flag: '',
};
// console.log(row);
// if (row.status === 'start') {
// form.status = 'departmentAudit';
// form.unitCount = row.num;
// }
// if (row.status === 'departmentAudit') {
// form.status = 'officeAudit';
// form.companyApprovalCount = row.num;
// }
// if (row.status === 'officeAudit') {
// form.status = 'officeSuppliesAudit';
// form.companyRealCount = row.num;
// }
// if (row.status === 'officeSuppliesAudit') {
// form.status = 'end';
// }
form.status = row.status;
form.unitCount = row.num;
form.companyApprovalCount = row.num;
form.companyRealCount = row.num;
console.log('审批表单', form);
if (
type === 'access' &&
!form.unitCount &&
!form.companyApprovalCount &&
!form.companyRealCount
) {
message.error('请输入审核或发放数量');
return;
}
form = { ...row, ...form };
if (type === 'reject') {
form.status = 'start';
form2Binding.value.columns = {};
title.value = '审核拒绝';
form2Binding.value.columns = {
comment: {
title: '',
key: 'comment',
col: { span: 24 },
colon: false,
component: {
name: 'a-textarea',
vModel: 'value',
autoSize: { minRows: 4, maxRows: 6 },
placeholder: '请输入',
},
},
};
temporaryModalApi.open();
return;
}
if (type === 'rejectConfirm') {
form.flag = 'rollback';
await Apis.officeSuppliesApply.post_audit({
data: [form, ...form2Ref.value.form],
});
message.success('操作成功');
triggerProxy('reload');
triggerProxy2('reload');
row.num = null;
return;
}
if (type === 'access') {
form.flag = 'submit';
await Apis.officeSuppliesApply.post_audit({ data: [form] });
message.success('操作成功');
triggerProxy('reload');
triggerProxy2('reload');
row.num = null;
}
}
onMounted(() => {});
</script>
<template>
<Page contentClass="h-full flex flex-col">
<a-space direction="vertical" size="small">
<a-card title="待办" size="small">
<Page content-class="h-full flex flex-col">
<TemporaryModal
:confirm-loading="isConfirmLoading"
:loading="isConfirmLoading"
:title="title"
class="w-[600px]"
content-class="min-h-0"
>
<fs-form ref="form2Ref" v-bind="form2Binding" />
</TemporaryModal>
<a-space class="flex h-full flex-col" direction="vertical" size="small">
<a-card
:body-style="{ flex: '1' }"
class="flex h-full flex-col"
size="small"
title="待办"
>
<vxe-grid ref="xGridRef" v-bind="gridOptions">
<template #toolbar_buttons> </template>
<template #edit_num="{ row }">
<a-input-number
v-model:value="row.num"
:addon-after="row.units || '个'"
:min="1"
placeholder="审核数量"
/>
</template>
<template #operate="{ row }">
<a-space>
<a-button
size="small"
type="text"
@click="handleAudit(row, 'access')"
>
通过
</a-button>
<a-button
danger
size="small"
type="text"
@click="handleAudit(row, 'reject')"
>
拒绝
</a-button>
</a-space>
</template>
</vxe-grid>
</a-card>
<a-card title="已办" size="small" contentClass="">
<a-card
:body-style="{ flex: '1' }"
class="flex h-full flex-col"
size="small"
title="已办"
>
<vxe-grid ref="xGrid2Ref" v-bind="grid2Options">
<template #toolbar_buttons> </template>
</vxe-grid>
@ -15,60 +254,8 @@
</Page>
</template>
<script setup lang="ts">
import { defineComponent, ref, reactive, onMounted } from 'vue';
import { type VxeGridProps } from 'vxe-table'
import { Page, useVbenModal } from '@vben/common-ui';
import { useVxeTable } from '#/hooks/vxeTable';
import Apis from '#/api'
import { getColumns } from './crud.tsx';
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const { xGridRef:xGrid2Ref, triggerProxy:triggerProxy2 } = useVxeTable({ ref: 'xGrid2Ref' });
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
columns: getColumns(),
proxyConfig: {
autoLoad: true,
ajax: {
query: ({ page }) => {
return Apis.officeSuppliesApply.get_page({ params: { pageNum: page.currentPage, pageSize: page.pageSize, } })
}
},
},
pagerConfig: {
enabled: true
},
toolbarConfig: {
enabled: false
},
}));
/** Hooks - 表格 */
const grid2Options = reactive(gridProps({
columns: getColumns(),
proxyConfig: {
autoLoad: true,
ajax: {
query: ({ page }) => {
return Apis.ccsq.get_donePage({ params: { pageNum: page.currentPage, pageSize: page.pageSize, } })
}
},
},
pagerConfig: {
enabled: true
},
toolbarConfig: {
enabled: false
},
}));
onMounted(() => {
})
</script>
<style></style>
<style scoped>
:deep(.ant-space-item) {
flex: 1;
}
</style>

View File

@ -1,82 +1,79 @@
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { useVbenModal } from "@vben/common-ui";
import { message } from "ant-design-vue";
import { useVxeTable } from "#/hooks/vxeTable";
import type { VxeGridPropTypes, VxeTablePropTypes } from "vxe-table";
import Apis from "#/api";
import { useRender } from "#/hooks/useRender";
import { MdiExport } from "@vben/icons";
import { Modal } from "ant-design-vue";
import { getUnitData } from "#/common/unit";
import Big from "big.js";
import type { VxeGridPropTypes, VxeTablePropTypes } from 'vxe-table';
import { reactive, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { MdiExport } from '@vben/icons';
import { message, Modal } from 'ant-design-vue';
import Big from 'big.js';
import Apis from '#/api';
import { getUnitData } from '#/common/unit';
import { useRender } from '#/hooks/useRender';
import { useVxeTable } from '#/hooks/vxeTable';
const emit = defineEmits<{
(e: "success"): void;
(e: "rowClick", row: any): any;
(e: "confirm", row: any[]): any[];
(e: 'confirm', row: any[]): any[];
(e: 'rowClick', row: any): any;
(e: 'success'): void;
}>();
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: "xGridRef" });
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
let isConfirmLoading = ref(false);
const isConfirmLoading = ref(false);
let unitId = ref();
const unitId = ref();
let treeData = ref<any>([])
const treeData = ref<any>([]);
const data = ref({
userIds: [],
});
const activeKey = ref('1');
const searchRef = ref()
const idToNameMap = ref<any>({});
function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: "seq", width: 50, fixed: "left" },
{ type: 'seq', width: 50, fixed: 'left' },
{
field: "officeName",
title: "办公用名称",
field: 'officeName',
title: '办公用名称',
minWidth: 180,
slots: {
default: ({ row }) => {
if (row.model) {
return useRender.renderText(row.officeName, "(" + row.model + ")");
return useRender.renderText(row.officeName, `(${row.model})`);
}
return useRender.renderText(row.officeName, "");
return useRender.renderText(row.officeName, '');
},
},
},
{
field: "count",
title: "采购数量",
field: 'count',
title: '采购数量',
minWidth: 180,
slots: {
default: ({ row }) => {
return useRender.renderText(row.count, row.units || "个");
return useRender.renderText(row.count, row.units || '个');
},
},
},
{
field: "price",
title: "单价",
field: 'price',
title: '单价',
minWidth: 180,
slots: {
default: ({ row }) => {
return useRender.renderText(row.price, "元");
return useRender.renderText(row.price, '元');
},
},
},
{
field: "priceSum",
title: "预计花费",
field: 'priceSum',
title: '预计花费',
minWidth: 180,
slots: {
default: ({ row }) => {
return useRender.renderText(row.priceSum, "元");
return useRender.renderText(row.priceSum, '元');
},
},
},
@ -86,52 +83,57 @@ function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
/** Hooks - 表格 */
const gridOptions = reactive(
gridProps({
height: "500px",
height: '500px',
columns: getColumns(),
proxyConfig: {
autoLoad: true,
ajax: {
query: async ({ page }) => {
let data = await Apis.officeSuppliesApplySum.get_list({
const data = await Apis.officeSuppliesApplySum.get_list({
params: {
pageNum: page.currentPage, pageSize: page.pageSize,
unitId: unitId.value
}
})
data.rows.forEach((item) => {
item.priceSum = Big(item.price||0).times(item.count||0).toNumber();
pageNum: page.currentPage,
pageSize: page.pageSize,
unitId: unitId.value,
},
});
return data
}
}
data.rows.forEach((item) => {
item.priceSum = Big(item.price || 0)
.times(item.count || 0)
.toNumber();
});
return data;
},
},
},
showFooter: true,
pagerConfig: {
enabled: false,
},
footerMethod: (e) => footerMethod(e),
})
}),
);
/**
* 表格计算规则
* @param param0
*/
const footerMethod: VxeTablePropTypes.FooterMethod<any> = ({ columns, data }) => {
const footerMethod: VxeTablePropTypes.FooterMethod<any> = ({
columns,
data,
}) => {
const footerData = [
columns.map((column, _columnIndex) => {
if (_columnIndex === 0) {
return "总价";
return '总价';
}
if (["priceSum"].includes(column.field)) {
return sumNum(data, column.field) + " 元";
if (['priceSum'].includes(column.field)) {
return `${sumNum(data, column.field)}`;
}
// if (['tradeType', 'companyName'].includes(column.field)) {
// return '';
// }
return "";
return '';
}),
];
return footerData;
@ -145,7 +147,7 @@ const footerMethod: VxeTablePropTypes.FooterMethod<any> = ({ columns, data }) =>
const sumNum = (list: any[], field: string) => {
let count = null;
list.forEach((item) => {
if (typeof item[field] == "number") {
if (typeof item[field] === 'number') {
if (count == null) {
count = new Big(0);
}
@ -155,7 +157,6 @@ const sumNum = (list: any[], field: string) => {
return count;
};
/**
* 办公用品批量选择确认事件
* @param rows
@ -163,13 +164,13 @@ const sumNum = (list: any[], field: string) => {
function handleChooseOfficeConfirm(rows) {
for (const row of rows) {
row.count = 1;
row.remarks = "";
row.remarks = '';
}
const $grid = xGridRef.value;
//
if ($grid) {
$grid.remove();
$grid.insert(rows).then(({ row }) => { });
$grid.insert(rows).then(({ row }) => {});
}
}
@ -177,42 +178,41 @@ function handleExport() {
const $grid = xGridRef.value;
if ($grid) {
$grid.exportData({
type: "xlsx",
type: 'xlsx',
});
message.success("导出成功");
message.success('导出成功');
}
}
const [BaseModal, baseModalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) {
if (isOpen) {
isConfirmLoading.value = false
isConfirmLoading.value = false;
treeData.value = await getUnitData()
treeData.value = await getUnitData();
}
},
onConfirm() {
try {
isConfirmLoading.value = true
isConfirmLoading.value = true;
const res = xGridRef.value?.getTableData();
const data = res?.fullData || [];
console.log(data);
data.forEach((item) => {
item.officeName = item.name || "";
item.officeName = item.name || '';
});
Modal.confirm({
title: "提示",
title: '提示',
content: `是否确认入库以上办公用品?`,
onOk: async () => {
try {
data.forEach((item) => {
item.status = "in";
item.status = 'in';
});
await Apis.inOrOut.post_saveBatch({ data: data });
emit("success");
await Apis.inOrOut.post_saveBatch({ data });
emit('success');
baseModalApi.close();
} catch (error) {
console.error(error);
@ -220,7 +220,7 @@ const [BaseModal, baseModalApi] = useVbenModal({
},
});
} finally {
isConfirmLoading.value = false
isConfirmLoading.value = false;
}
},
@ -230,12 +230,16 @@ const [BaseModal, baseModalApi] = useVbenModal({
});
</script>
<template>
<BaseModal :title="'办公用品审批通过的采购汇总单'" :showConfirmButton="false" cancelText="关闭">
<div class="h-full flex flex-col">
<BaseModal
:show-confirm-button="false"
cancel-text="关闭"
title="办公用品审批通过的采购汇总单"
>
<div class="flex h-full flex-col">
<VxeGrid ref="xGridRef" v-bind="gridOptions">
<template #toolbar_buttons>
<vben-button variant="primary" @click="handleExport()">
<MdiExport class="text-lg mr-0.5" />
<MdiExport class="mr-0.5 text-lg" />
导出
</vben-button>
<!-- <a-tree-select v-model:value="unitId" show-search style="width: 200px"
@ -255,7 +259,9 @@ const [BaseModal, baseModalApi] = useVbenModal({
<template #supplies_default_slot="scope">
<div>
{{ scope.row.name ? scope.row.name + "" + scope.row.model + "" : "" }}
{{
scope.row.name ? `${scope.row.name}${scope.row.model}` : ''
}}
</div>
</template>
</VxeGrid>

View File

@ -1,14 +1,17 @@
<script lang="ts" setup>
import { nextTick, ref } from "vue";
import { useVbenModal } from "@vben/common-ui";
import Apis from "#/api";
import { message } from "ant-design-vue";
import { nextTick, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import Apis from '#/api';
const emit = defineEmits<{
(e: "success"): void;
(e: 'success'): void;
}>();
let isConfirmLoading = ref(false);
const isConfirmLoading = ref(false);
const data = ref({
isUpdate: false,
@ -18,63 +21,60 @@ const formRef = ref();
const formBinding = ref({
col: { span: 24 },
labelCol: { style: { width: "120px" } },
labelCol: { style: { width: '120px' } },
initialForm: {},
columns: {
name: {
title: "用品名称",
key: "name",
title: '用品名称',
key: 'name',
component: {
name: "a-input",
vModel: "value",
name: 'a-input',
vModel: 'value',
allowClear: true,
},
rules: [{ required: true, message: "请输入办公用品名称" }],
rules: [{ required: true, message: '请输入办公用品名称' }],
},
model: {
title: "型号",
key: "model",
title: '型号',
key: 'model',
component: {
name: "a-input",
vModel: "value",
name: 'a-input',
vModel: 'value',
allowClear: true,
},
},
qualityStandard: {
title: "质量标准",
key: "qualityStandard",
title: '质量标准',
key: 'qualityStandard',
component: {
name: "a-input",
vModel: "value",
name: 'a-input',
vModel: 'value',
allowClear: true,
},
},
price: {
title: "单价",
key: "price",
title: '单价',
key: 'price',
component: {
name: "a-input-number",
vModel: "value",
name: 'a-input-number',
vModel: 'value',
allowClear: true,
min: 0,
addonAfter: "元",
placeholder: "请输入办公用品单价",
addonAfter: '元',
placeholder: '请输入办公用品单价',
},
rules: [{ required: true, message: "请输入办公用品单价" }],
rules: [{ required: true, message: '请输入办公用品单价' }],
},
remarks: {
title: "备注",
key: "remarks",
title: '备注',
key: 'remarks',
component: {
name: "a-textarea",
vModel: "value",
name: 'a-textarea',
vModel: 'value',
allowClear: true,
},
},
},
doSubmit(context: any) {
console.log(context);
},
});
const [Modal, modalApi] = useVbenModal({
@ -90,19 +90,19 @@ const [Modal, modalApi] = useVbenModal({
}
},
async onConfirm() {
console.info("onConfirm");
console.info('onConfirm');
try {
await formRef.value!.submit();
isConfirmLoading.value = true;
let form = formRef.value!.form;
const form = formRef.value!.form;
await Apis.officeSuppliesList.post_saveBatch({ data: [form] });
modalApi.close();
message.success("保存成功");
emit("success");
message.success('保存成功');
emit('success');
} catch (error) {
console.log(error);
message.error("保存失败");
message.error('保存失败');
} finally {
isConfirmLoading.value = false;
}
@ -114,10 +114,10 @@ const [Modal, modalApi] = useVbenModal({
</script>
<template>
<Modal
:title="data.isUpdate ? '编辑办公用品' : '新增办公用品'"
:loading="isConfirmLoading"
:confirm-loading="isConfirmLoading"
:loading="isConfirmLoading"
:title="data.isUpdate ? '编辑办公用品' : '新增办公用品'"
>
<fs-form ref="formRef" v-bind="formBinding"> </fs-form>
<fs-form ref="formRef" v-bind="formBinding" />
</Modal>
</template>

View File

@ -1,101 +1,107 @@
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { useVbenModal } from "@vben/common-ui";
import { message } from "ant-design-vue";
import { useVxeTable } from "#/hooks/vxeTable";
import type { VxeGridPropTypes } from "vxe-table";
import Apis from "#/api";
import { useRender } from "#/hooks/useRender";
import { MdiAdd } from "@vben/icons";
import { Modal } from "ant-design-vue";
import { getUnitData } from "#/common/unit";
import chooseOfficeModal from "../inventory/choose-office-modal.vue";
import type { VxeGridPropTypes } from 'vxe-table';
import { reactive, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Modal } from 'ant-design-vue';
import Apis from '#/api';
import { getUnitData } from '#/common/unit';
import { useRender } from '#/hooks/useRender';
import { useVxeTable } from '#/hooks/vxeTable';
import chooseOfficeModal from '../inventory/choose-office-modal.vue';
const emit = defineEmits<{
(e: 'confirm', row: any[]): any[];
(e: 'rowClick', row: any): any;
(e: 'success'): void;
}>();
const [ChooseOfficeModal, chooseOfficeModalApi] = useVbenModal({
connectedComponent: chooseOfficeModal,
});
const emit = defineEmits<{
(e: "success"): void;
(e: "rowClick", row: any): any;
(e: "confirm", row: any[]): any[];
}>();
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: "xGridRef" });
const isConfirmLoading = ref(false);
let isConfirmLoading = ref(false);
const unitId = ref();
let unitId = ref();
let treeData = ref<any>([])
const treeData = ref<any>([]);
const data = ref({
userIds: [],
});
const activeKey = ref('1');
const searchRef = ref()
const searchRef = ref();
const idToNameMap = ref<any>({});
function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: "seq", width: 50, fixed: "left" },
{ field: "applyName", title: "申请人", width: 120 },
{ type: 'seq', width: 50, fixed: 'left' },
{ field: 'applyName', title: '申请人', width: 120 },
{
field: "applyInfo",
title: "申请信息",
field: 'applyInfo',
title: '申请信息',
minWidth: 200,
slots: { default: "applyInfoSlot" },
slots: { default: 'applyInfoSlot' },
},
{
field: "count",
title: "申请数量",
field: 'count',
title: '申请数量',
width: 100,
showOverflow: true,
slots: {
default: ({ row }) => {
return useRender.renderText(row.count, row.units || "个");
return useRender.renderText(row.count, row.units || '个');
},
},
},
{
field: "unitCount",
title: "部门审核量",
field: 'unitCount',
title: '部门审核量',
width: 100,
showOverflow: true,
slots: {
default: ({ row }) => {
return useRender.renderText(row.unitCount, row.units || "个");
return useRender.renderText(row.unitCount, row.units || '个');
},
},
},
{
field: "companyApprovalCount",
title: "办公室审核量",
field: 'companyApprovalCount',
title: '办公室审核量',
width: 100,
showOverflow: true,
slots: {
default: ({ row }) => {
return useRender.renderText(row.companyApprovalCount, row.units || "个");
return useRender.renderText(
row.companyApprovalCount,
row.units || '个',
);
},
},
},
{ field: "remarks", title: "申请原因", minWidth: 150 },
{ field: "time", title: "申请时间", width: 150 },
{ field: 'remarks', title: '申请原因', minWidth: 150 },
{ field: 'time', title: '申请时间', width: 150 },
{
field: "num",
title: "发放数量",
width: 150,
fixed: "right",
slots: { default: "edit_num" },
field: 'num',
title: '发放数量',
width: 100,
fixed: 'right',
slots: { default: 'edit_num' },
},
{
field: "operate",
title: "操作",
field: 'operate',
title: '操作',
width: 120,
fixed: "right",
slots: { default: "operate" },
fixed: 'right',
slots: { default: 'operate' },
},
];
}
@ -103,7 +109,7 @@ function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
/** Hooks - 表格 */
const gridOptions = reactive(
gridProps({
height: "500px",
height: '500px',
columns: getColumns(),
proxyConfig: {
autoLoad: true,
@ -111,21 +117,22 @@ const gridOptions = reactive(
query: ({ page }) => {
return Apis.officeSuppliesApply.get_page({
params: {
pageNum: page.currentPage, pageSize: page.pageSize,
unitId: unitId.value
}
})
}
}
pageNum: page.currentPage,
pageSize: page.pageSize,
unitId: unitId.value,
},
});
},
},
},
rowConfig: {
isCurrent: false,
},
editConfig: {
trigger: "click",
mode: "row",
trigger: 'click',
mode: 'row',
},
})
}),
);
/**
@ -133,14 +140,7 @@ const gridOptions = reactive(
*/
function handleCellClick({ row }) {
console.log(row);
emit("rowClick", row);
}
/**
* 打开选择办公用品弹窗
*/
function handleOpenOfficeModal() {
chooseOfficeModalApi.open();
emit('rowClick', row);
}
/**
@ -150,13 +150,13 @@ function handleOpenOfficeModal() {
function handleChooseOfficeConfirm(rows) {
for (const row of rows) {
row.count = 1;
row.remarks = "";
row.remarks = '';
}
const $grid = xGridRef.value;
//
if ($grid) {
$grid.remove();
$grid.insert(rows).then(({ row }) => { });
$grid.insert(rows).then(({ row }) => {});
}
}
@ -169,37 +169,35 @@ function handleAudit(row, type) {
}
}
let title = ref("选择要入库的办公用品");
const title = ref('选择要入库的办公用品');
const [BaseModal, baseModalApi] = useVbenModal({
async onOpenChange(isOpen: boolean) {
if (isOpen) {
isConfirmLoading.value = false
isConfirmLoading.value = false;
treeData.value = await getUnitData()
treeData.value = await getUnitData();
}
},
onConfirm() {
try {
isConfirmLoading.value = true
isConfirmLoading.value = true;
const res = xGridRef.value?.getTableData();
const data = res?.fullData || [];
console.log(data);
data.forEach((item) => {
item.officeName = item.name || "";
item.officeName = item.name || '';
});
Modal.confirm({
title: "提示",
title: '提示',
content: `是否确认入库以上办公用品?`,
onOk: async () => {
try {
data.forEach((item) => {
item.status = "in";
item.status = 'in';
});
await Apis.inOrOut.post_saveBatch({ data: data });
emit("success");
await Apis.inOrOut.post_saveBatch({ data });
emit('success');
baseModalApi.close();
} catch (error) {
console.error(error);
@ -207,7 +205,7 @@ const [BaseModal, baseModalApi] = useVbenModal({
},
});
} finally {
isConfirmLoading.value = false
isConfirmLoading.value = false;
}
},
@ -218,19 +216,32 @@ const [BaseModal, baseModalApi] = useVbenModal({
</script>
<template>
<BaseModal :title="title">
<ChooseOfficeModal
class="w-[900px] max-w-[75vw]"
@confirm="handleChooseOfficeConfirm"
/>
<ChooseOfficeModal class="w-[900px] max-w-[75vw]" @confirm="handleChooseOfficeConfirm" />
<a-tabs v-model:activeKey="activeKey" size="small">
<a-tabs v-model:active-key="activeKey" size="small">
<a-tab-pane key="1" tab="针对审批分发">
<div class="h-full flex flex-col">
<VxeGrid ref="xGridRef" v-bind="gridOptions" @cell-click="handleCellClick">
<div class="flex h-full flex-col">
<VxeGrid
ref="xGridRef"
v-bind="gridOptions"
@cell-click="handleCellClick"
>
<template #toolbar_buttons>
<a-tree-select v-model:value="unitId" show-search style="width: 200px"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" placeholder="可筛选单位" allow-clear
tree-default-expand-all :tree-data="treeData" tree-node-filter-prop="label"
@change="triggerProxy('reload')">
<a-tree-select
v-model:value="unitId"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:tree-data="treeData"
allow-clear
placeholder="可筛选单位"
show-search
style="width: 200px"
tree-default-expand-all
tree-node-filter-prop="label"
@change="triggerProxy('reload')"
>
<template #title="{ value: val, label }">
<b v-if="val === 'parent 1-1'" style="color: #08c">sss</b>
<template v-else>{{ label }}</template>
@ -244,21 +255,39 @@ const [BaseModal, baseModalApi] = useVbenModal({
<template #supplies_default_slot="scope">
<div>
{{ scope.row.name ? scope.row.name + "" + scope.row.model + "" : "" }}
{{
scope.row.name
? `${scope.row.name}${scope.row.model}`
: ''
}}
</div>
</template>
<template #edit_num="{ row }">
<a-input-number v-model:value="row.count" :min="1" :addon-after="row.units || ''"
placeholder="请选择发放数量" />
<a-input-number
v-model:value="row.count"
:addon-after="row.units || '个'"
:min="1"
placeholder="请选择发放数量"
/>
</template>
<template #operate="{ row }">
<a-space>
<a-button type="text" size="small" class="text-primary-500" @click="handleAudit(row, 'access')">
<a-button
class="text-primary-500"
size="small"
type="text"
@click="handleAudit(row, 'access')"
>
发放
</a-button>
<a-button type="text" size="small" class="text-red-500" @click="handleAudit(row, 'reject')">
<a-button
class="text-red-500"
size="small"
type="text"
@click="handleAudit(row, 'reject')"
>
驳回
</a-button>
</a-space>
@ -267,8 +296,5 @@ const [BaseModal, baseModalApi] = useVbenModal({
</div>
</a-tab-pane>
</a-tabs>
</BaseModal>
</template>

View File

@ -1,28 +1,20 @@
<script setup lang="ts">
<script setup lang="tsx">
import { nextTick, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Page, useVbenModal } from '@vben/common-ui';
import { MdiUpload } from '@vben/icons';
import { useUserStore } from '@vben/stores';
import { dict } from '@fast-crud/fast-crud';
import {
message,
Modal,
type UploadChangeParam,
type UploadProps,
} from 'ant-design-vue';
import { message, Modal, type UploadChangeParam } from 'ant-design-vue';
import { logger } from 'common-utils';
import dayjs, { Dayjs } from 'dayjs';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { FileUploader } from '#/utils/file';
import chooseUserModal from '#/views/system/user/choose-user-modal.vue';
const { xGridRef, gridProps, triggerProxy } = useVxeTable({ ref: 'xGridRef' });
const fileUploader = new FileUploader({});
const [ChooseUserModal, chooseUserModalApi] = useVbenModal({
@ -31,16 +23,12 @@ const [ChooseUserModal, chooseUserModalApi] = useVbenModal({
const router = useRouter();
const route = useRoute();
const id = route.params.id;
const showHelpTip = ref(false);
const id = ref(route.params.id);
const containerRef = ref();
const formRef = ref();
const form2Ref = ref();
const isLoading = ref(false);
function handleUpdateFormattedValue() {
const { starttime, endtime } = formRef.value.form;
@ -54,6 +42,7 @@ function handleUpdateFormattedValue() {
}
}
const selectUsers = ref<any>([]);
const disabledDate = (current: Dayjs) => {
// Can not select days before today and today
const form = formRef.value.form;
@ -80,7 +69,7 @@ const formBinding = ref({
key: 'taskType',
col: { span: 12 },
component: {
name: 'fs-dict-select',
name: 'fs-dict-radio',
vModel: 'value',
dict: dict({
data: getDictOptions(DICT_TYPE.supervise_task_type),
@ -93,14 +82,13 @@ const formBinding = ref({
key: 'urgentDegree',
col: { span: 12 },
component: {
name: 'fs-dict-select',
name: 'fs-dict-radio',
vModel: 'value',
dict: dict({
data: getDictOptions(DICT_TYPE.supervise_emergency_level),
}),
},
},
starttime: {
title: '开始时间',
key: 'starttime',
@ -113,7 +101,7 @@ const formBinding = ref({
// disabledDate: (current) => current && current < dayjs().endOf("day"),
format: 'YYYY-MM-DD HH:mm',
valueFormat: 'YYYY-MM-DD HH:mm',
onChange: (e) => {
onChange: () => {
handleUpdateFormattedValue();
},
},
@ -129,7 +117,7 @@ const formBinding = ref({
allowClear: false,
showTime: { format: 'HH:mm' },
disabledDate,
onChange: (e) => {
onChange: () => {
handleUpdateFormattedValue();
},
format: 'YYYY-MM-DD HH:mm',
@ -146,7 +134,7 @@ const formBinding = ref({
allowClear: false,
showTime: { format: 'HH:mm' },
disabledDate,
onChange: (e) => {
onChange: () => {
handleUpdateFormattedValue();
},
format: 'YYYY-MM-DD HH:mm',
@ -161,6 +149,7 @@ const formBinding = ref({
component: {
name: 'a-textarea',
vModel: 'value',
autoSize: { minRows: 4, maxRows: 6 },
},
rules: [{ required: true, message: '请输入任务内容' }],
},
@ -171,6 +160,7 @@ const formBinding = ref({
component: {
name: 'a-textarea',
vModel: 'value',
autoSize: { minRows: 4, maxRows: 6 },
},
rules: [{ required: true, message: '请输入任务进度' }],
},
@ -178,13 +168,35 @@ const formBinding = ref({
title: '相关附件',
key: 'fileList',
},
people: {
title: '相关执行人',
key: 'people',
component: {
name: 'a-select',
vModel: 'value',
open: false,
mode: 'multiple',
onClick: () => {
chooseUserModalApi.setData({
title: '选择执行人',
limitMultipleNum: 10,
userIds: selectUsers.value.map((row) => row.value) || [],
});
chooseUserModalApi.open();
},
onChange: () => {
// selectUsers
selectUsers.value = formRef.value.form.people.map((item1) => {
const value = item1.split('-')[1];
return selectUsers.value.find((item2) => item2.value === value);
});
},
},
// rules: [{ required: true, message: '' }],
},
},
});
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
return false;
};
function handleBack() {
Modal.confirm({
title: '提示',
@ -215,7 +227,7 @@ function handleDelete() {
okType: 'danger',
onOk: async () => {
await Apis.meeting.post_deletes({
params: { ids: currData.value.guid },
params: { ids: id.value },
});
message.success('删除成功');
back();
@ -228,53 +240,97 @@ function handleDelete() {
* @param rows
*/
function handleChooseUserConfirm(rows) {
console.log('[ rows ] >', rows);
rows.forEach((element) => {});
const $grid = xGridRef.value;
//
if ($grid) {
$grid.remove();
$grid.insert(rows).then(({ row }) => {});
} else {
console.error('xGridRef不存在');
}
rows.forEach((row) => {
row.label = row.EMPLOYEE_NAME;
row.value = row.ACCOUNT_ID;
});
formRef.value.setFormData({
people: rows.map((row) => `${row.label}-${row.value}`),
});
selectUsers.value = rows;
}
async function handleSubmit() {
async function handleSave() {
isLoading.value = true;
try {
const form = formRef.value.form;
// await formRef.value.submit()
console.log(formRef.value);
const userStore = useUserStore();
await formRef.value.submit();
let newForm = {};
const newForm = formRef.value.form;
//
//
const fileList = formRef.value.form.fileList;
let files: any = [];
if (fileList && fileList.length > 0) {
files = await fileUploader.upload(fileList, { source: 'erp' });
}
console.log(files);
if (files) {
newForm.fileUuid = (files.map((item) => item.fileUuid) || []).join(',');
}
newForm = Object.assign({}, formRef.value.form, newForm);
delete newForm.fileList;
console.log(newForm);
const data = await Apis.supervise.post_save({
data: newForm,
});
id.value = data.guid;
await Apis.supervise.post_save({ data: newForm }).then((data) => {
message.success('提交成功');
back();
message.success('保存成功');
Modal.confirm({
title: '提示',
content: '保存成功!是否立即提交?',
onOk: () => {
handleSubmit();
},
onCancel: () => {
back();
},
});
} catch (error) {
logger.error('立项保存失败', error);
} finally {
isLoading.value = false;
}
}
async function handleSubmit() {
isLoading.value = true;
try {
await formRef.value.submit();
const newForm: any = formRef.value.form;
if (!newForm.people || newForm.people.length === 0) {
message.error('请先选择相关执行人');
return;
}
await Apis.supervise.post_audit({
params: {
guid: id.value,
},
});
if (newForm.people && newForm.people.length > 0) {
const people = selectUsers.value.map((item) => {
return {
dwbm: item.ORG_ID,
dwmc: item.ORG_NAME,
principalId: item.ACCOUNT_ID,
principal: item.EMPLOYEE_NAME,
};
});
await Apis.feedback.post_save({
params: { guid: newForm.guid },
data: people,
});
}
message.success('提交成功');
back();
} catch (error) {
logger.error('立项提交失败', error);
message.error('提交失败,请稍候再试');
console.log(error);
} finally {
isLoading.value = false;
}
@ -284,14 +340,11 @@ const currData = ref({});
onMounted(async () => {
isLoading.value = true;
console.log(id);
try {
if (id) {
let data = await Apis.supervise.get_page({ params: { guid: id } });
if (id.value) {
let data = await Apis.supervise.get_page({ params: { guid: id.value } });
data = data.rows[0];
console.log(data);
currData.value = data;
nextTick(() => {
formRef.value.setFormData(data);
@ -299,7 +352,6 @@ onMounted(async () => {
if (data.fileUuid) {
const files = await fileUploader.select(data.fileUuid);
console.log(files);
nextTick(() => {
formRef.value.setFormData({
fileList: files,
@ -308,7 +360,7 @@ onMounted(async () => {
}
}
} catch (error) {
console.log(error);
logger.error('当前立项信息不存在', error);
Modal.error({
title: '提示',
@ -336,6 +388,9 @@ onMounted(async () => {
/>
<a-spin :spinning="isLoading">
<a-space>
<vben-button variant="primary" @click="handleSave()">
保存
</vben-button>
<vben-button variant="primary" @click="handleSubmit()">
提交
</vben-button>
@ -353,7 +408,7 @@ onMounted(async () => {
<template #form_fileList="scope">
<a-upload
v-model:file-list="scope.form.fileList"
:before-upload="beforeUpload"
:before-upload="() => false"
:max-count="3"
accept=".pdf,.ppt,.pptx"
name="file"
@ -373,15 +428,4 @@ onMounted(async () => {
</Page>
</template>
<style scoped>
.sortable-tree-demo .drag-btn {
cursor: move;
font-size: 12px;
text-align: center;
}
.sortable-tree-demo .vxe-body--row.sortable-ghost,
.sortable-tree-demo .vxe-body--row.sortable-chosen {
background-color: #dfecfb;
}
</style>
<style scoped></style>

View File

@ -110,7 +110,7 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
},
},
{ field: 'remarks', title: '备注', minWidth: 200 },
// { title: '操作', width: 120, fixed: 'right', slots: { default: 'operate' } }
{ title: '操作', width: 80, fixed: 'right', slots: { default: 'operate' } },
];
}

View File

@ -0,0 +1,82 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { getColumns } from '../../feedback/crud';
const { xGridRef, gridProps, triggerProxy } = useVxeTable({ ref: 'xGridRef' });
const open = ref<boolean>(false);
const title = ref('');
const currData = ref<any>({});
/** Hooks - 表格 */
const gridOptions = reactive(
gridProps({
height: '100%',
columns: getColumns(),
proxyConfig: {
autoLoad: false,
ajax: {
query: async () => {
const data = await Apis.feedback.get_page({
params: {
pageNum: 1,
pageSize: 500,
guid: currData.value.guid,
},
});
return data;
},
},
},
pagerConfig: {
enabled: true,
},
toolbarConfig: {
enabled: true,
},
}),
);
// function handleExport() {
// const $grid = xGridRef.value;
// if ($grid) {
// $grid.exportData({
// type: 'xlsx',
// });
// message.success('');
// }
// }
const openDrawer = async (data) => {
open.value = true;
title.value = data.title;
currData.value = data.record;
triggerProxy('reload');
};
const closeDrawer = () => {
open.value = false;
};
defineExpose({ open: openDrawer, close: closeDrawer });
</script>
<template>
<a-drawer
:open="open"
:title="title"
height="60vh"
placement="bottom"
@close="closeDrawer"
>
<template #extra>
<a-button @click="closeDrawer">关闭</a-button>
</template>
<vxe-grid ref="xGridRef" v-bind="gridOptions">
<template #toolbar_buttons> </template>
</vxe-grid>
</a-drawer>
</template>

View File

@ -18,12 +18,14 @@ import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { getColumns, getFormSchema } from './crud.tsx';
import DetailDrawer from './detail-drawer/detail-drawer.vue';
const router = useRouter();
const isOneDay = ref('');
const searchRef = ref();
const detailDrawerRef = ref();
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
@ -87,6 +89,13 @@ function handleCellClick({ row }) {
setSelectRow(row);
}
function selectDetail(row) {
detailDrawerRef.value.open({
title: row.taskName,
record: row,
});
}
onMounted(() => {
triggerProxy('reload');
});
@ -122,6 +131,8 @@ function handleDelete(row) {
<template>
<Page content-class="h-full flex flex-col">
<DetailDrawer ref="detailDrawerRef" />
<fs-search ref="searchRef" v-bind="searchForm" />
<div class="min-h-300px flex-1">
<vxe-grid
@ -172,6 +183,18 @@ function handleDelete(row) {
<template #statusSlot="{ row }">
<a-tag>{{ row.status || '待提交' }}</a-tag>
</template>
<template #operate="{ row }">
<a-space>
<a-button
class="text-primary"
size="small"
type="text"
@click="selectDetail(row)"
>
查看
</a-button>
</a-space>
</template>
</vxe-grid>
</div>
</Page>

View File

@ -1,24 +1,36 @@
import type { VxeGridPropTypes } from 'vxe-table';
import { useRender } from '#/hooks/useRender';
import dayjs from 'dayjs';
import { DICT_TYPE, getDictObj, getDictOptions } from '#/utils/dict';
import { dict } from '@fast-crud/fast-crud';
import { unitComponentProps } from '#/common/unit';
import { useRender } from '#/hooks/useRender';
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
export const PrimaryKey = 'guid';
export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
return [
{ type: 'radio', width: 40, slots: { radio: 'radio_cell' }, align: 'center', fixed: 'left' },
{
type: 'radio',
width: 40,
slots: { radio: 'radio_cell' },
align: 'center',
fixed: 'left',
},
// { type: 'expand', width: 60, slots: { content: 'expand_content' } },
{
field: 'TASK_NAME', title: '任务标题', width: 200, slots: {
default: "task-name-slot"
field: 'TASK_NAME',
title: '任务标题',
width: 200,
slots: {
default: 'task-name-slot',
},
},
{
field: 'status', title: '任务状态', width: 120, slots: {
default: "statusSlot"
field: 'status',
title: '任务状态',
width: 120,
slots: {
default: 'statusSlot',
},
},
{
@ -27,22 +39,31 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
width: 100,
slots: {
default: ({ row }) => {
return useRender.renderDict(row.taskType, DICT_TYPE.supervise_task_type);
}
}
},
{
field: 'TASK_CONTENT', title: '任务内容', width: 300, slots: {
default: ({ row }) => {
return useRender.renderMultiLineText(row.TASK_CONTENT, {});
}
return useRender.renderDict(
row.taskType,
DICT_TYPE.supervise_task_type,
);
},
},
},
{
field: 'taskProgress', title: '任务进度', minWidth: 200, slots: {
field: 'TASK_CONTENT',
title: '任务内容',
width: 300,
slots: {
default: ({ row }) => {
return useRender.renderMultiLineText(row.TASK_CONTENT, {});
},
},
},
{
field: 'taskProgress',
title: '任务进度',
minWidth: 200,
slots: {
default: ({ row }) => {
return useRender.renderMultiLineText(row.taskProgress, {});
}
},
},
},
{
@ -51,33 +72,42 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
width: 100,
slots: {
default: ({ row }) => {
return useRender.renderDict(row.urgentDegree, DICT_TYPE.supervise_emergency_level);
}
}
return useRender.renderDict(
row.urgentDegree,
DICT_TYPE.supervise_emergency_level,
);
},
},
},
{
field: 'starttime', title: '开始日期', width: 120,
field: 'starttime',
title: '开始日期',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDate(row.starttime, 'YYYY-MM-DD');
}
}
},
},
},
{
field: 'endtime', title: '截止日期', width: 120,
field: 'endtime',
title: '截止日期',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDate(row.endtime, 'YYYY-MM-DD');
}
}
},
},
},
{
field: 'planFinishTime', title: '预计完成日期', width: 120,
field: 'planFinishTime',
title: '预计完成日期',
width: 120,
slots: {
default: ({ row }) => {
return useRender.renderDate(row.planFinishTime, 'YYYY-MM-DD');
}
}
},
},
},
{ field: 'remarks', title: '备注', minWidth: 200 },
// { title: '操作', width: 120, fixed: 'right', slots: { default: 'operate' } }
@ -86,8 +116,7 @@ export function getColumns(params: any = {}): VxeGridPropTypes.Columns {
export function getFormSchema(_params: any = {}) {
return {
initialForm: {
},
initialForm: {},
columns: {
taskName: {
title: '任务名称',
@ -112,21 +141,21 @@ export function getFormSchema(_params: any = {}) {
data: [
{
value: '1',
label: '未开始'
label: '未开始',
},
{
value: '2',
label: '进行中'
label: '进行中',
},
{
value: '3',
label: '已完成'
label: '已完成',
},
{
value: '4',
label: '已超时'
}
]
label: '已超时',
},
],
}),
},
autoSearchTrigger: 'enter',
@ -141,7 +170,7 @@ export function getFormSchema(_params: any = {}) {
class: 'min-w-[180px]',
allowClear: true,
dict: dict({
data: getDictOptions(DICT_TYPE.supervise_task_type)
data: getDictOptions(DICT_TYPE.supervise_task_type),
}),
},
autoSearchTrigger: 'enter',
@ -155,6 +184,5 @@ export function getFormSchema(_params: any = {}) {
// show: true,
// },
},
}
};
}

View File

@ -1,55 +1,55 @@
<script setup lang="ts">
import { defineComponent, ref, computed, reactive, onMounted } from 'vue';
import { FsCrud } from '@fast-crud/fast-crud';
import { type VxeGridProps } from 'vxe-table'
import { Page, useVbenModal } from '@vben/common-ui';
import { computed, onMounted, reactive, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { MdiExport, MdiRadioChecked, MdiRadioUnchecked } from '@vben/icons';
import { message } from 'ant-design-vue';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
import { MdiAdd, MdiUpdate, MdiDelete, MdiImport, MdiExport, MdiRadioUnchecked, MdiRadioChecked } from '@vben/icons';
import { getFormSchema, getColumns } from './crud.tsx';
import { dict } from "@fast-crud/fast-crud";
import { getMonthStartAndEnd } from '#/utils/time'
import Apis from '#/api'
import dayjs from 'dayjs';
import { message } from "ant-design-vue";
import { Modal } from 'ant-design-vue';
import { unitComponentProps } from '#/common/unit'
import { DICT_TYPE, getDictOptions } from '#/utils/dict';
import { useRouter } from 'vue-router'
import { getColumns, getFormSchema } from './crud.tsx';
const router = useRouter();
const searchRef = ref()
const searchRef = ref();
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
/** Hooks - 表格 */
const gridOptions = reactive(gridProps({
columns: getColumns(),
proxyConfig: {
autoLoad: false,
ajax: {
query: ({ page }) => {
let form = searchRef.value?.formData
return Apis.supervise.get_huizong({ params: { pageNum: page.currentPage, pageSize: page.pageSize, ...form } })
}
const gridOptions = reactive(
gridProps({
columns: getColumns(),
proxyConfig: {
autoLoad: false,
ajax: {
query: ({ page }) => {
const form = searchRef.value?.formData;
return Apis.supervise.get_huizong({
params: {
pageNum: page.currentPage,
pageSize: page.pageSize,
...form,
},
});
},
},
},
},
pagerConfig: {
enabled: true
},
toolbarConfig: {
enabled: true
},
}));
pagerConfig: {
enabled: true,
},
toolbarConfig: {
enabled: true,
},
}),
);
function handleExport() {
const $grid = xGridRef.value;
if ($grid) {
$grid.exportData({
type: "xlsx",
type: 'xlsx',
});
message.success("导出成功");
message.success('导出成功');
}
}
@ -60,7 +60,7 @@ const selectRow: any = computed(() => {
/** 单选框选中事件 */
const setSelectRow = (row: any) => {
if (selectRow.value && selectRow.value['guid'] === row['guid']) {
if (selectRow.value && selectRow.value.guid === row.guid) {
xGridRef.value?.clearRadioRow();
} else {
xGridRef.value?.setRadioRow(row);
@ -73,31 +73,30 @@ function handleCellClick({ row }) {
}
onMounted(() => {
triggerProxy('reload')
})
triggerProxy('reload');
});
let searchParams = reactive({})
const searchForm = ref({
...getFormSchema(),
onSearch(context: any) {
console.log(searchRef.value)
triggerProxy('reload')
},
onReset(context: any) {
searchParams = context.form
onSearch(_context: any) {
triggerProxy('reload');
},
});
</script>
<template>
<Page contentClass="h-full flex flex-col">
<fs-search ref="searchRef" v-bind="searchForm"> </fs-search>
<div class="flex-1 min-h-300px">
<vxe-grid ref="xGridRef" v-bind="gridOptions" @cell-click="handleCellClick">
<Page content-class="h-full flex flex-col">
<fs-search ref="searchRef" v-bind="searchForm" />
<div class="min-h-300px flex-1">
<vxe-grid
ref="xGridRef"
v-bind="gridOptions"
@cell-click="handleCellClick"
>
<template #toolbar_buttons>
<a-space>
<vben-button variant="primary" @click="handleExport()">
<MdiExport class="text-lg mr-0.5" />
<MdiExport class="mr-0.5 text-lg" />
导出
</vben-button>
</a-space>
@ -115,7 +114,7 @@ const searchForm = ref({
</template>
<template #statusSlot="{ row }">
<a-tag>{{ row.status || "待提交" }}</a-tag>
<a-tag>{{ row.status || '待提交' }}</a-tag>
</template>
</vxe-grid>
</div>

View File

@ -1,15 +1,15 @@
<script lang="ts" setup>
import { reactive, ref } from "vue";
import { useVbenModal } from "@vben/common-ui";
import { message } from "ant-design-vue";
import { useVxeTable } from "#/hooks/vxeTable";
import type { VxeGridPropTypes } from "vxe-table";
import Apis from "#/api";
import type { VxeGridPropTypes } from 'vxe-table';
const emit = defineEmits<{
(e: "rowClick", row: any): any;
(e: "confirm", row: any[]): any[];
}>();
import { reactive, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message } from 'ant-design-vue';
import { logger } from 'common-utils';
import Apis from '#/api';
import { useVxeTable } from '#/hooks/vxeTable';
const props = withDefaults(
defineProps<{
@ -19,68 +19,75 @@ const props = withDefaults(
{
multiple: false,
showDepartment: true,
}
},
);
const emit = defineEmits<{
(e: 'confirm', row: any[]): any[];
(e: 'rowClick', row: any): any;
}>();
const [messageApi] = message.useMessage();
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: "xGridRef" });
const { xGridRef, triggerProxy, gridProps } = useVxeTable({ ref: 'xGridRef' });
const searchRef = ref();
const data = ref({
userIds: []
title: '',
limitMultipleNum: 10,
userIds: [],
});
const formRef = ref();
const treeData = ref([]);
const treeItemKey = ref([]);
const isEditMenu = ref(false);
const checkedId = ref(0);
const checkRecords = ref([]);
const canMultiple = ref(false);
const limitMultipleNum = ref(10);
const checkRecords = ref<any>([]);
const searchBinding = ref({
initialForm: {},
columns: {
name: {
title: "用户姓名",
key: "name",
title: '用户姓名',
key: 'name',
component: {
name: "a-input",
vModel: "value",
name: 'a-input',
vModel: 'value',
allowClear: true,
},
show: true,
},
},
onSearch(context: any) {
triggerProxy("reload");
onSearch(_context: any) {
triggerProxy('reload');
},
onReset(context: any) { },
});
function getColumns(_params: any = {}): VxeGridPropTypes.Columns {
let columns: any[] = [{ type: "seq", width: 50 }];
let columns: any[] = [{ type: 'seq', width: 50 }];
if (props.multiple) {
columns.push({ type: "checkbox", title: "用户ID", width: 150 });
columns.push({ type: 'checkbox', title: '用户ID', width: 150 });
}
columns = columns.concat([
{ field: "EMPLOYEE_NAME", title: "用户姓名", width: 120 },
{ field: "EMPLOYEE_GENDER", title: "性别", width: 80 },
{ field: "MAIL_ADDRESS", title: "邮箱", width: 150 },
{ field: "PHONE_NUM", title: "联系方式", width: 120 },
{ field: "ORG_NAME", title: "所属组织", minWidth: 120 },
]);
columns = [
...columns,
{ field: 'EMPLOYEE_NAME', title: '用户姓名', width: 120 },
{ field: 'EMPLOYEE_GENDER', title: '性别', width: 80 },
{ field: 'MAIL_ADDRESS', title: '邮箱', width: 150 },
{ field: 'PHONE_NUM', title: '联系方式', width: 120 },
{ field: 'ORG_NAME', title: '所属组织', minWidth: 120 },
];
return columns;
}
/** Hooks - 表格 */
const gridOptions = reactive(
gridProps({
height: "400px",
height: '400px',
columns: getColumns(),
pagerConfig: { size: "mini" },
pagerConfig: { size: 'mini' },
proxyConfig: {
autoLoad: true,
ajax: {
@ -101,10 +108,10 @@ const gridOptions = reactive(
},
},
rowConfig: {
keyField: "ACCOUNT_ID",
keyField: 'ACCOUNT_ID',
},
checkboxConfig: {
labelField: "ACCOUNT_ID",
labelField: 'ACCOUNT_ID',
//
showHeader: false,
highlight: true,
@ -112,14 +119,19 @@ const gridOptions = reactive(
//
reserve: true,
checkMethod: ({ row }) => {
let checkRecordIds = checkRecords.value.map((item) => item.ACCOUNT_ID);
if (checkRecords.value.length < 10 || checkRecordIds.includes(row.ACCOUNT_ID)) {
const checkRecordIds = checkRecords.value.map(
(item) => item.ACCOUNT_ID,
);
if (
checkRecords.value.length < limitMultipleNum.value ||
checkRecordIds.includes(row.ACCOUNT_ID)
) {
return true;
}
return false;
},
},
})
}),
);
//
@ -143,7 +155,7 @@ function transTree(list) {
const children = list.filter((data) => data.PARENT_ID === item.ORG_ID);
// return
if (!children.length) return;
if (children.length === 0) return;
// (item) children
item.children = children;
@ -159,37 +171,37 @@ function transferFormData() {
subFilter: [] as any[],
};
let values = searchRef.value?.formData;
const values = searchRef.value?.formData;
if (values.name) {
form.subFilter.push({
symbol: "like",
singleValue: `%${values.name || ""}%`,
key: "EMPLOYEE_NAME",
symbol: 'like',
singleValue: `%${values.name || ''}%`,
key: 'EMPLOYEE_NAME',
});
}
if (values.id) {
form.subFilter.push({
symbol: "like",
singleValue: `%${values.id || ""}%`,
key: "ACCOUNT_ID",
symbol: 'like',
singleValue: `%${values.id || ''}%`,
key: 'ACCOUNT_ID',
});
}
if (form.subFilter.length == 2) {
form.subFilter[1].logic = "AND";
if (form.subFilter.length === 2) {
form.subFilter[1].logic = 'AND';
}
let newForm: any = {};
const newForm: any = {};
if (treeItemKey.value && treeItemKey.value.length) {
if (treeItemKey.value && treeItemKey.value.length > 0) {
newForm.subFilter = [
{
symbol: "like",
symbol: 'like',
singleValue: treeItemKey.value[0],
key: "ORG_ID",
logic: "AND",
key: 'ORG_ID',
logic: 'AND',
},
];
@ -204,22 +216,22 @@ function transferFormData() {
async function loadDataByDept() {
try {
let data = await Apis.api.core.orgemplbc.organization.post_paging({
const data = await Apis.api.core.orgemplbc.organization.post_paging({
params: { page: 1, size: 1000 },
data: {
subFilter: [
{
symbol: "like",
singleValue: "0001%",
key: "ORG_ID",
logic: "AND",
symbol: 'like',
singleValue: '0001%',
key: 'ORG_ID',
logic: 'AND',
},
],
},
});
treeData.value = transTree(data.rows);
} catch (error) {
console.log(err);
logger.error('loadDataByDept error', error);
}
}
@ -227,8 +239,7 @@ async function loadDataByDept() {
* 单元行点击事件
*/
function handleCellClick({ row }) {
console.log(row);
emit("rowClick", {
emit('rowClick', {
...row,
label: row.EMPLOYEE_NAME,
value: row.ACCOUNT_ID,
@ -238,11 +249,10 @@ function handleCellClick({ row }) {
const currTypeData = ref<any>({});
const treeItemTitle = ref("");
const treeItemTitle = ref('');
function handleTreeSelectChange(keys, opts) {
console.log("keys", keys, opts);
if (keys.length) {
function handleTreeSelectChange(keys) {
if (keys.length > 0) {
const treeItem = keys[0];
treeItemKey.value = keys;
treeItemTitle.value = treeItem.label;
@ -250,15 +260,15 @@ function handleTreeSelectChange(keys, opts) {
Object.assign(currTypeData.value, treeItem);
isEditMenu.value = true;
checkedId.value = treeItem.id;
triggerProxy("reload");
triggerProxy('reload');
} else {
currTypeData.value = {};
checkedId.value = 0;
isEditMenu.value = false;
treeItemKey.value = [];
treeItemTitle.value = "";
treeItemTitle.value = '';
// searchParams.type = undefined;
triggerProxy("reload");
triggerProxy('reload');
}
}
/**
@ -266,14 +276,14 @@ function handleTreeSelectChange(keys, opts) {
* @param e
*/
function handleCheckboxChange(e) {
let currRows = xGridRef.value?.getCheckboxRecords() || [];
let otherRows = xGridRef.value?.getCheckboxReserveRecords() || [];
let allRows = [...currRows, ...otherRows];
console.log("e>", e);
console.log("allRows", allRows);
const currRows = xGridRef.value?.getCheckboxRecords() || [];
const otherRows = xGridRef.value?.getCheckboxReserveRecords() || [];
const allRows = [...currRows, ...otherRows];
if (e.checked) {
const existingIds = new Set(checkRecords.value.map((item) => item.ACCOUNT_ID));
const existingIds = new Set(
checkRecords.value.map((item) => item.ACCOUNT_ID),
);
if (!existingIds.has(e.row.ACCOUNT_ID)) {
checkRecords.value = [...checkRecords.value, e.row];
@ -282,43 +292,46 @@ function handleCheckboxChange(e) {
handleCloseTag(e.row);
}
console.log("[ checkRecords.value ] >", checkRecords.value);
if (allRows.length >= 10) {
messageApi.warning("最多只能选择10条数据");
console.log('[ checkRecords.value ] >', checkRecords.value);
if (allRows.length >= limitMultipleNum.value) {
messageApi.warning(`最多只能选择${limitMultipleNum.value}条数据`);
}
}
function handleCloseTag(row) {
checkRecords.value = checkRecords.value.filter(
(item) => item.ACCOUNT_ID != row.ACCOUNT_ID
(item) => item.ACCOUNT_ID != row.ACCOUNT_ID,
);
xGridRef.value?.setCheckboxRow(row, false);
}
let title = ref("选择人员");
const title = ref('选择人员');
const [Modal, modalApi] = useVbenModal({
onOpenChange(isOpen: boolean) {
if (isOpen) {
data.value = modalApi.getData<Record<string, any>>() || {};
if (data.value.title) {
title.value = data.value.title
title.value = data.value.title;
}
console.log(data.value.userIds)
let rows: any = []
// canMultiple.value = data.value.multiple || false;
if (data.value.limitMultipleNum) {
limitMultipleNum.value = data.value.limitMultipleNum;
}
console.log(data.value.userIds);
const rows: any = [];
for (const element of checkRecords.value) {
if (data.value.userIds.includes(element.ACCOUNT_ID)) {
rows.push(element)
rows.push(element);
}
}
checkRecords.value = rows
checkRecords.value = rows;
//
loadDataByDept();
}
},
onConfirm() {
console.info("onConfirm");
emit("confirm", checkRecords.value);
emit('confirm', checkRecords.value);
modalApi.close();
},
onCancel() {
@ -328,11 +341,16 @@ const [Modal, modalApi] = useVbenModal({
</script>
<template>
<Modal :title="title">
<div v-if="props.multiple" class="flex flex-row py-12px">
<span class="block mr-12px">已选择</span>
<div class="flex flex-row flex-1">
<div v-if="props.multiple || canMultiple" class="py-12px flex flex-row">
<span class="mr-12px block">已选择</span>
<div class="flex flex-1 flex-row">
<a-space>
<a-tag v-for="(item, index) in checkRecords" :key="index" closable @close="handleCloseTag(item)">
<a-tag
v-for="(item, index) in checkRecords"
:key="index"
closable
@close="handleCloseTag(item)"
>
{{ item.EMPLOYEE_NAME }}-{{ item.ACCOUNT_ID }}
</a-tag>
</a-space>
@ -340,14 +358,24 @@ const [Modal, modalApi] = useVbenModal({
</div>
<a-row class="h-full">
<a-col :span="5" class="">
<a-tree class="draggable-tree" block-node autoExpandParent :tree-data="treeData" @select="handleTreeSelectChange" />
<a-tree
:tree-data="treeData"
auto-expand-parent
block-node
class="draggable-tree"
@select="handleTreeSelectChange"
/>
</a-col>
<a-col :span="19" class="flex flex-col">
<fs-search ref="searchRef" v-bind="searchBinding"> </fs-search>
<div class="flex-1 min-h-300px">
<VxeGrid ref="xGridRef" class="h-420px" v-bind="gridOptions" @cell-click="handleCellClick"
@checkbox-change="handleCheckboxChange">
</VxeGrid>
<fs-search ref="searchRef" v-bind="searchBinding" />
<div class="min-h-300px flex-1">
<VxeGrid
ref="xGridRef"
class="h-420px"
v-bind="gridOptions"
@cell-click="handleCellClick"
@checkbox-change="handleCheckboxChange"
/>
</div>
</a-col>
</a-row>

View File

@ -164,6 +164,9 @@ export default {
get_toDoPage: (data?: QueryOptions) => http.get('/app/ccsq/toDoPage', data),
/** 协同办公/出差申请 已办 */
get_donePage: (data?: QueryOptions) => http.get('/app/ccsq/donePage', data),
/** 协同办公/出差申请 获取可退回节点信息 */
get_getBackNode: (data?: QueryOptions) =>
http.get('/app/ccsq/getBackNode', data),
/** 协同办公/出差申请 查询流程节点 */
get_getFlowNodeUserConfig: (data?: QueryOptions) =>
http.get('/app/ccsq/getFlowNodeUserConfig', data),
@ -384,6 +387,12 @@ export default {
/** 合同系统/申报 退回 */
post_rollback: (data?: BodyOptions) =>
http.post('/app/sbCtrBasePt/rollback', data),
/** 合同系统/申报 发起废除 */
post_repeal: (data?: BodyOptions) =>
http.post('/app/sbCtrBasePt/repeal', data),
/** 合同系统/申报 发起流程 */
post_start: (data?: BodyOptions) =>
http.post('/app/sbCtrBasePt/start', data),
},
contractBaseInfo: {
/** 合同系统/立项 合同立项保存 */
@ -780,4 +789,12 @@ export default {
/** 合同系统/首页待办/已办 待办 */
get_todo: (data?: QueryOptions) => http.get('/app/home/todo', data),
},
sqConsignPt: {
/** 合同系统/签约授权 签约授权保存 */
post_saveSignMultiEntity: (data?: BodyOptions) =>
http.post('/app/sqConsignPt/saveSignMultiEntity', data),
/** 合同系统/签约授权 签约依据查询 */
get_SigningaAuthorizationSerch: (data?: QueryOptions) =>
http.get('/app/sqConsignPt/SigningaAuthorizationSerch', data),
},
};