feat: collaboration and statistics
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
import { http } from "@/utils/http";
|
||||
|
||||
export type SettlementStatusValue =
|
||||
| "NONE"
|
||||
| "SETTLED"
|
||||
| "UNSETTLED"
|
||||
| "PARTIAL";
|
||||
export type CollaborationFileType = "GOODS_IMAGE" | "ATTACHMENT";
|
||||
|
||||
export interface SettlementStatusDTO {
|
||||
status: SettlementStatusValue;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface CollaborationRecordListCommand extends BasePageQuery {
|
||||
brand?: string;
|
||||
goods?: string;
|
||||
cooperationPlatform?: string;
|
||||
purchaseBeginTime?: string;
|
||||
purchaseEndTime?: string;
|
||||
}
|
||||
|
||||
export interface CollaborationTaskCommand {
|
||||
releaseDate?: string;
|
||||
}
|
||||
|
||||
export interface CollaborationExpenditureCommand {
|
||||
spendDate?: string;
|
||||
amount?: number;
|
||||
purpose?: string;
|
||||
}
|
||||
|
||||
export interface CollaborationSettlementCommand {
|
||||
settleDate?: string;
|
||||
method?: string;
|
||||
income?: number;
|
||||
purpose?: string;
|
||||
}
|
||||
|
||||
export interface CollaborationFileCommand {
|
||||
fileType: CollaborationFileType;
|
||||
url: string;
|
||||
fileName?: string;
|
||||
newFileName?: string;
|
||||
originalFilename?: string;
|
||||
}
|
||||
|
||||
export interface AddCollaborationRecordCommand {
|
||||
brand: string;
|
||||
goods: string;
|
||||
cooperationPlatform?: string;
|
||||
imageReturnNum: number;
|
||||
retainedMethod?: string;
|
||||
cooperatedMethod?: string;
|
||||
purchaseMethod?: string;
|
||||
purchasePrice?: number;
|
||||
purchaseDate?: string;
|
||||
purchasePlatform?: string;
|
||||
deadline?: string;
|
||||
remuneration?: number;
|
||||
completeDate?: string;
|
||||
requirements?: string;
|
||||
remark?: string;
|
||||
tasks: CollaborationTaskCommand[];
|
||||
expenditures: CollaborationExpenditureCommand[];
|
||||
settlements: CollaborationSettlementCommand[];
|
||||
files: CollaborationFileCommand[];
|
||||
}
|
||||
|
||||
export interface UpdateCollaborationRecordCommand
|
||||
extends AddCollaborationRecordCommand {
|
||||
recordId: number;
|
||||
}
|
||||
|
||||
export interface CollaborationRecordPageResponse {
|
||||
recordId: number;
|
||||
brand: string;
|
||||
goods: string;
|
||||
cooperationPlatform?: string;
|
||||
imageReturnNum: number;
|
||||
retainedMethod?: string;
|
||||
cooperatedMethod?: string;
|
||||
purchaseMethod?: string;
|
||||
purchasePrice?: number;
|
||||
purchaseDate?: string;
|
||||
purchasePlatform?: string;
|
||||
deadline?: string;
|
||||
remuneration?: number;
|
||||
completeDate?: string;
|
||||
requirements?: string;
|
||||
remark?: string;
|
||||
tasksNum: number;
|
||||
completedTasksNum: number;
|
||||
purchaseSettlementStatus: SettlementStatusDTO;
|
||||
deliverySettlementStatus: SettlementStatusDTO;
|
||||
remunerationSettlementStatus: SettlementStatusDTO;
|
||||
createTime: string;
|
||||
}
|
||||
|
||||
export interface CollaborationRecordDetailResponse
|
||||
extends CollaborationRecordPageResponse {
|
||||
tasks: Array<
|
||||
CollaborationTaskCommand & { taskId?: number; sortOrder?: number }
|
||||
>;
|
||||
expenditures: Array<
|
||||
CollaborationExpenditureCommand & { expenditureId?: number }
|
||||
>;
|
||||
settlements: Array<
|
||||
CollaborationSettlementCommand & { settlementId?: number }
|
||||
>;
|
||||
files: Array<
|
||||
CollaborationFileCommand & { fileId?: number; sortOrder?: number }
|
||||
>;
|
||||
}
|
||||
|
||||
export interface CollaborationOptionResponse {
|
||||
type: string;
|
||||
label: string;
|
||||
values: string[];
|
||||
}
|
||||
|
||||
export interface CollaborationMonthlyStatisticsResponse {
|
||||
month: number;
|
||||
purchasePrice: number;
|
||||
expenditureAmount: number;
|
||||
settledRemuneration: number;
|
||||
settledTotal: number;
|
||||
}
|
||||
|
||||
export interface UploadResponse {
|
||||
url: string;
|
||||
fileName: string;
|
||||
newFileName: string;
|
||||
originalFilename: string;
|
||||
}
|
||||
|
||||
export const getCollaborationRecordListApi = (
|
||||
params: CollaborationRecordListCommand
|
||||
) => {
|
||||
return http.request<ResponseData<PageDTO<CollaborationRecordPageResponse>>>(
|
||||
"get",
|
||||
"/collaboration/record/list",
|
||||
{ params }
|
||||
);
|
||||
};
|
||||
|
||||
export const getCollaborationRecordInfoApi = (recordId: number) => {
|
||||
return http.request<ResponseData<CollaborationRecordDetailResponse>>(
|
||||
"get",
|
||||
`/collaboration/record/${recordId}`
|
||||
);
|
||||
};
|
||||
|
||||
export const addCollaborationRecordApi = (
|
||||
data: AddCollaborationRecordCommand
|
||||
) => {
|
||||
return http.request<ResponseData<void>>("post", "/collaboration/record", {
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
export const updateCollaborationRecordApi = (
|
||||
data: UpdateCollaborationRecordCommand
|
||||
) => {
|
||||
return http.request<ResponseData<void>>("put", "/collaboration/record", {
|
||||
data
|
||||
});
|
||||
};
|
||||
|
||||
export const deleteCollaborationRecordApi = (data: Array<number>) => {
|
||||
return http.request<ResponseData<void>>("delete", "/collaboration/record", {
|
||||
params: {
|
||||
ids: data.toString()
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const getCollaborationOptionsApi = () => {
|
||||
return http.request<ResponseData<CollaborationOptionResponse[]>>(
|
||||
"get",
|
||||
"/collaboration/record/options"
|
||||
);
|
||||
};
|
||||
|
||||
export const getCollaborationMonthlyStatisticsApi = (year: number) => {
|
||||
return http.request<ResponseData<CollaborationMonthlyStatisticsResponse[]>>(
|
||||
"get",
|
||||
"/collaboration/record/monthly-statistics",
|
||||
{
|
||||
params: { year }
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const uploadCollaborationFileApi = (data: FormData) => {
|
||||
return http.request<ResponseData<UploadResponse>>(
|
||||
"post",
|
||||
"/file/upload",
|
||||
{ data },
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -89,6 +89,7 @@ function handleClose(
|
||||
:key="index"
|
||||
v-bind="options"
|
||||
v-model="options.visible"
|
||||
:align-center="options.alignCenter ?? true"
|
||||
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
|
||||
@close="handleClose(options, index)"
|
||||
@opened="eventsCallBack('open', options, index)"
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
class="header-btn"
|
||||
/>
|
||||
<el-button
|
||||
:icon="Close"
|
||||
:icon="closeIcon"
|
||||
link
|
||||
@click="handleCloseClick"
|
||||
class="header-btn"
|
||||
@@ -81,7 +81,8 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { ElDialog, ElButton, ElScrollbar } from "element-plus";
|
||||
import { DialogEmits, DialogProps } from "./dialog";
|
||||
import { Close } from "@element-plus/icons-vue";
|
||||
import Close from "@iconify-icons/ep/close";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import FullScreenMaximize from "@/assets/svg/FullScreenMaximize.svg?component";
|
||||
import FullScreenMinimize from "@/assets/svg/FullScreenMinimize.svg?component";
|
||||
|
||||
@@ -96,6 +97,7 @@ const props = withDefaults(defineProps<DialogProps>(), {
|
||||
loading: false
|
||||
});
|
||||
const emits = defineEmits<DialogEmits>();
|
||||
const closeIcon = useRenderIcon(Close);
|
||||
|
||||
const visible = computed<boolean>({
|
||||
get: () => {
|
||||
@@ -107,7 +109,6 @@ const visible = computed<boolean>({
|
||||
const fullScreenState = ref(!!props.initFullScreen);
|
||||
const fullScreen = computed<boolean>({
|
||||
get: () => {
|
||||
console.log("fullScreen getter", props.fullScreen, fullScreenState.value);
|
||||
// 非受控模式,状态完全由组件内部控制
|
||||
if (props.fullScreen === undefined) {
|
||||
return fullScreenState.value;
|
||||
@@ -117,7 +118,6 @@ const fullScreen = computed<boolean>({
|
||||
},
|
||||
set: v => {
|
||||
fullScreenState.value = v;
|
||||
console.log("fullScreen setter", v, props.fullScreen);
|
||||
// 受控模式,将状态更新到父组件
|
||||
if (props.fullScreen !== undefined) {
|
||||
emits("update:fullScreen", v);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import path from "path";
|
||||
import { getConfig } from "@/config";
|
||||
import { menuType } from "../../types";
|
||||
import extraIcon from "./extraIcon.vue";
|
||||
@@ -174,10 +175,7 @@ function resolvePath(routePath) {
|
||||
return routePath || props.basePath;
|
||||
} else {
|
||||
// 使用path.posix.resolve替代path.resolve 避免windows环境下使用electron出现盘符问题
|
||||
const segments = `${props.basePath}/${routePath}`
|
||||
.split("/")
|
||||
.filter(Boolean);
|
||||
return `/${segments.join("/")}`;
|
||||
return path.posix.resolve(props.basePath, routePath);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -69,11 +69,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
.el-overlay-dialog:has(.pure-dialog.el-dialog:not(.is-fullscreen)) {
|
||||
box-sizing: border-box;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pure-dialog {
|
||||
&.el-dialog:not(.is-fullscreen) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: min(88vh, calc(100vh - 48px));
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
.pure-dialog-svg {
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
|
||||
.el-dialog__header,
|
||||
.el-dialog__footer {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
top: 20px;
|
||||
right: 14px;
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
<script setup lang="ts">
|
||||
import { h, ref } from "vue";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
import { useCollaborationRecordHook } from "./utils/hook";
|
||||
import { CollaborationRecordPageResponse } from "@/api/collaboration/record";
|
||||
import RecordFormModal from "./record-form-modal.vue";
|
||||
|
||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
|
||||
defineOptions({
|
||||
name: "CollaborationRecord"
|
||||
});
|
||||
|
||||
const tableRef = ref();
|
||||
const searchFormRef = ref();
|
||||
|
||||
const {
|
||||
searchFormParams,
|
||||
pageLoading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
defaultSort,
|
||||
deadlineRange,
|
||||
purchaseRange,
|
||||
optionMap,
|
||||
multipleSelection,
|
||||
onSearch,
|
||||
onSortChanged,
|
||||
getRecordList,
|
||||
resetForm,
|
||||
handleDelete,
|
||||
handleBulkDelete
|
||||
} = useCollaborationRecordHook();
|
||||
|
||||
const recordFormRef = ref();
|
||||
|
||||
function openDialog(
|
||||
type: "add" | "update",
|
||||
row?: CollaborationRecordPageResponse
|
||||
) {
|
||||
addDialog({
|
||||
title: type === "add" ? "新增合作记录" : "编辑合作记录",
|
||||
props: {
|
||||
type,
|
||||
row,
|
||||
optionMap
|
||||
},
|
||||
width: "1180px",
|
||||
top: "6vh",
|
||||
draggable: true,
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(RecordFormModal as any, { ref: recordFormRef }),
|
||||
beforeSure: async done => {
|
||||
const isSuccess = await recordFormRef.value.handleConfirm();
|
||||
if (isSuccess) {
|
||||
done();
|
||||
onSearch(tableRef);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-form
|
||||
ref="searchFormRef"
|
||||
:inline="true"
|
||||
:model="searchFormParams"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
|
||||
>
|
||||
<el-form-item label="品牌" prop="brand">
|
||||
<el-input
|
||||
v-model="searchFormParams.brand"
|
||||
placeholder="请输入品牌"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="物品" prop="goods">
|
||||
<el-input
|
||||
v-model="searchFormParams.goods"
|
||||
placeholder="请输入物品"
|
||||
clearable
|
||||
class="!w-[180px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="合作平台" prop="cooperationPlatform">
|
||||
<el-select
|
||||
v-model="searchFormParams.cooperationPlatform"
|
||||
placeholder="请选择合作平台"
|
||||
clearable
|
||||
class="!w-[160px]"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in optionMap.cooperationPlatform"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="预完成日期">
|
||||
<el-date-picker
|
||||
v-model="deadlineRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="购入日期">
|
||||
<el-date-picker
|
||||
v-model="purchaseRange"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
class="!w-[240px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(Search)"
|
||||
:loading="pageLoading"
|
||||
@click="onSearch(tableRef)"
|
||||
>
|
||||
搜索
|
||||
</el-button>
|
||||
<el-button
|
||||
:icon="useRenderIcon(Refresh)"
|
||||
@click="resetForm(searchFormRef, tableRef)"
|
||||
>
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="合作记录"
|
||||
:columns="columns"
|
||||
@refresh="onSearch(tableRef)"
|
||||
>
|
||||
<template #buttons>
|
||||
<el-button
|
||||
type="primary"
|
||||
:icon="useRenderIcon(AddFill)"
|
||||
@click="openDialog('add')"
|
||||
>
|
||||
新增合作
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
@click="handleBulkDelete(tableRef)"
|
||||
>
|
||||
批量删除
|
||||
</el-button>
|
||||
</template>
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<pure-table
|
||||
border
|
||||
ref="tableRef"
|
||||
align-whole="center"
|
||||
showOverflowTooltip
|
||||
table-layout="auto"
|
||||
:loading="pageLoading"
|
||||
:size="size"
|
||||
adaptive
|
||||
:data="dataList"
|
||||
:columns="dynamicColumns"
|
||||
:default-sort="defaultSort"
|
||||
:pagination="pagination"
|
||||
:paginationSmall="size === 'small'"
|
||||
:header-cell-style="{
|
||||
background: 'var(--el-table-row-hover-bg-color)',
|
||||
color: 'var(--el-text-color-primary)'
|
||||
}"
|
||||
@page-size-change="getRecordList"
|
||||
@page-current-change="getRecordList"
|
||||
@sort-change="onSortChanged"
|
||||
@selection-change="
|
||||
rows => (multipleSelection = rows.map(item => item.recordId))
|
||||
"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(EditPen)"
|
||||
@click="openDialog('update', row)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-popconfirm
|
||||
:title="`是否确认删除编号为${row.recordId}的合作记录`"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="danger"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</pure-table>
|
||||
</template>
|
||||
</PureTableBar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.search-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,558 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { ElMessage, FormInstance, FormRules } from "element-plus";
|
||||
import type {
|
||||
UploadFile,
|
||||
UploadRequestOptions,
|
||||
UploadUserFile
|
||||
} from "element-plus";
|
||||
import Plus from "@iconify-icons/ep/plus";
|
||||
import {
|
||||
AddCollaborationRecordCommand,
|
||||
CollaborationFileCommand,
|
||||
CollaborationFileType,
|
||||
CollaborationRecordPageResponse,
|
||||
UpdateCollaborationRecordCommand,
|
||||
addCollaborationRecordApi,
|
||||
getCollaborationRecordInfoApi,
|
||||
updateCollaborationRecordApi,
|
||||
uploadCollaborationFileApi
|
||||
} from "@/api/collaboration/record";
|
||||
|
||||
interface Props {
|
||||
type: "add" | "update";
|
||||
row?: CollaborationRecordPageResponse;
|
||||
optionMap: Record<string, string[]>;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const defaultFormData = (): AddCollaborationRecordCommand &
|
||||
Partial<UpdateCollaborationRecordCommand> => ({
|
||||
recordId: 0,
|
||||
brand: "",
|
||||
goods: "",
|
||||
cooperationPlatform: "小红书",
|
||||
imageReturnNum: 1,
|
||||
retainedMethod: "寄拍",
|
||||
cooperatedMethod: "水下",
|
||||
purchaseMethod: "拍单",
|
||||
purchasePrice: undefined,
|
||||
purchaseDate: "",
|
||||
purchasePlatform: "",
|
||||
deadline: "",
|
||||
remuneration: undefined,
|
||||
completeDate: "",
|
||||
requirements: "",
|
||||
remark: "",
|
||||
tasks: [{ releaseDate: "" }],
|
||||
expenditures: [],
|
||||
settlements: [],
|
||||
files: []
|
||||
});
|
||||
|
||||
const formData = reactive(defaultFormData());
|
||||
const formRef = ref<FormInstance>();
|
||||
const previewImageUrl = ref("");
|
||||
const isImagePreviewVisible = ref(false);
|
||||
|
||||
const rules: FormRules = {
|
||||
brand: [{ required: true, message: "品牌不能为空" }],
|
||||
goods: [{ required: true, message: "物品不能为空" }],
|
||||
cooperationPlatform: [{ required: true, message: "合作平台不能为空" }],
|
||||
imageReturnNum: [{ required: true, message: "返图数量不能为空" }],
|
||||
deadline: [{ required: true, message: "预完成日期不能为空" }]
|
||||
};
|
||||
|
||||
const goodsImages = computed(() =>
|
||||
formData.files.filter(item => item.fileType === "GOODS_IMAGE")
|
||||
);
|
||||
const attachments = computed(() =>
|
||||
formData.files.filter(item => item.fileType === "ATTACHMENT")
|
||||
);
|
||||
const goodsImageUploadFiles = computed<UploadUserFile[]>(() =>
|
||||
goodsImages.value.map(file => ({
|
||||
name: getFileName(file),
|
||||
url: getFileUrl(file)
|
||||
}))
|
||||
);
|
||||
const attachmentUploadFiles = computed<UploadUserFile[]>(() =>
|
||||
attachments.value.map(file => ({
|
||||
name: getFileName(file),
|
||||
url: getFileUrl(file)
|
||||
}))
|
||||
);
|
||||
|
||||
async function handleOpened() {
|
||||
resetFormData();
|
||||
if (props.type === "update" && props.row?.recordId) {
|
||||
await loadDetail(props.row.recordId);
|
||||
}
|
||||
}
|
||||
|
||||
function resetFormData() {
|
||||
Object.assign(formData, defaultFormData());
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
async function loadDetail(recordId: number) {
|
||||
const { data } = await getCollaborationRecordInfoApi(recordId);
|
||||
Object.assign(formData, {
|
||||
...defaultFormData(),
|
||||
...data,
|
||||
tasks: data.tasks.length ? data.tasks : [{ releaseDate: "" }],
|
||||
expenditures: data.expenditures,
|
||||
settlements: data.settlements,
|
||||
files: data.files
|
||||
});
|
||||
}
|
||||
|
||||
function addTask() {
|
||||
formData.tasks.push({ releaseDate: "" });
|
||||
}
|
||||
|
||||
function removeTask(index: number) {
|
||||
formData.tasks.splice(index, 1);
|
||||
}
|
||||
|
||||
function addExpenditure() {
|
||||
formData.expenditures.push({
|
||||
spendDate: "",
|
||||
amount: undefined,
|
||||
purpose: ""
|
||||
});
|
||||
}
|
||||
|
||||
function removeExpenditure(index: number) {
|
||||
formData.expenditures.splice(index, 1);
|
||||
}
|
||||
|
||||
function addSettlement() {
|
||||
formData.settlements.push({
|
||||
settleDate: "",
|
||||
method: "",
|
||||
income: undefined,
|
||||
purpose: ""
|
||||
});
|
||||
}
|
||||
|
||||
function removeSettlement(index: number) {
|
||||
formData.settlements.splice(index, 1);
|
||||
}
|
||||
|
||||
async function handleUpload(
|
||||
option: UploadRequestOptions,
|
||||
fileType: CollaborationFileType
|
||||
) {
|
||||
const data = new FormData();
|
||||
data.append("file", option.file);
|
||||
const response = await uploadCollaborationFileApi(data);
|
||||
formData.files.push(toFileCommand(response.data, fileType));
|
||||
option.onSuccess(response.data);
|
||||
}
|
||||
|
||||
function toFileCommand(file, fileType: CollaborationFileType) {
|
||||
return {
|
||||
fileType,
|
||||
url: file.url,
|
||||
fileName: file.fileName,
|
||||
newFileName: file.newFileName,
|
||||
originalFilename: file.originalFilename
|
||||
};
|
||||
}
|
||||
|
||||
function removeFile(file: CollaborationFileCommand) {
|
||||
const index = formData.files.indexOf(file);
|
||||
if (index >= 0) {
|
||||
formData.files.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePreviewImage(file: UploadFile) {
|
||||
if (!file.url) return;
|
||||
previewImageUrl.value = file.url;
|
||||
isImagePreviewVisible.value = true;
|
||||
}
|
||||
|
||||
function handleRemoveGoodsImage(file: UploadFile) {
|
||||
const target = goodsImages.value.find(item => getFileUrl(item) === file.url);
|
||||
if (target) {
|
||||
removeFile(target);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePreviewAttachment(file: UploadFile) {
|
||||
if (!file.url) {
|
||||
ElMessage.warning("文件地址不存在");
|
||||
return;
|
||||
}
|
||||
window.open(file.url, "_blank", "noopener,noreferrer");
|
||||
}
|
||||
|
||||
function handleRemoveAttachment(file: UploadFile) {
|
||||
const target = attachments.value.find(item => getFileUrl(item) === file.url);
|
||||
if (target) {
|
||||
removeFile(target);
|
||||
}
|
||||
}
|
||||
|
||||
function getFileName(file: CollaborationFileCommand) {
|
||||
return file.originalFilename || file.newFileName || "未命名文件";
|
||||
}
|
||||
|
||||
function getFileUrl(file: CollaborationFileCommand) {
|
||||
if (file.url) return file.url;
|
||||
if (!file.fileName) return "";
|
||||
return `${import.meta.env.VITE_APP_BASE_API}${file.fileName}`;
|
||||
}
|
||||
|
||||
async function handleConfirm() {
|
||||
const isValid = await formRef.value?.validate().catch(() => false);
|
||||
if (!isValid) return false;
|
||||
return submitForm();
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
try {
|
||||
if (props.type === "add") {
|
||||
await addCollaborationRecordApi(formData);
|
||||
} else {
|
||||
await updateCollaborationRecordApi(
|
||||
formData as UpdateCollaborationRecordCommand
|
||||
);
|
||||
}
|
||||
ElMessage.success("提交成功");
|
||||
return true;
|
||||
} catch (e) {
|
||||
ElMessage.error((e as Error)?.message || "提交失败");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(handleOpened);
|
||||
|
||||
defineExpose({ handleConfirm });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
class="record-form"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-width="112px"
|
||||
>
|
||||
<el-tabs>
|
||||
<el-tab-pane label="基本信息">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="brand" label="品牌" required>
|
||||
<el-input v-model="formData.brand" placeholder="请输入品牌" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="goods" label="物品" required>
|
||||
<el-input v-model="formData.goods" placeholder="请输入物品" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="cooperationPlatform" label="合作平台" required>
|
||||
<el-select v-model="formData.cooperationPlatform" clearable>
|
||||
<el-option
|
||||
v-for="item in optionMap.cooperationPlatform"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="imageReturnNum" label="返图数量" required>
|
||||
<el-input-number
|
||||
:min="1"
|
||||
controls-position="right"
|
||||
v-model="formData.imageReturnNum"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="留存方式">
|
||||
<el-select v-model="formData.retainedMethod">
|
||||
<el-option
|
||||
v-for="item in optionMap.retainedMethod"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合作方式">
|
||||
<el-select v-model="formData.cooperatedMethod">
|
||||
<el-option
|
||||
v-for="item in optionMap.cooperatedMethod"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="购入方式">
|
||||
<el-select v-model="formData.purchaseMethod">
|
||||
<el-option
|
||||
v-for="item in optionMap.purchaseMethod"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="购入金额">
|
||||
<el-input-number
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
v-model="formData.purchasePrice"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="购入平台">
|
||||
<el-select v-model="formData.purchasePlatform" clearable>
|
||||
<el-option
|
||||
v-for="item in optionMap.purchasePlatform"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="购入日期">
|
||||
<el-date-picker
|
||||
v-model="formData.purchaseDate"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="deadline" label="预完成日期" required>
|
||||
<el-date-picker
|
||||
v-model="formData.deadline"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="稿费">
|
||||
<el-input-number
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
v-model="formData.remuneration"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="物品图片">
|
||||
<el-upload
|
||||
multiple
|
||||
accept="image/*"
|
||||
class="goods-image-upload"
|
||||
list-type="picture-card"
|
||||
:file-list="goodsImageUploadFiles"
|
||||
:http-request="option => handleUpload(option, 'GOODS_IMAGE')"
|
||||
:on-preview="handlePreviewImage"
|
||||
:on-remove="handleRemoveGoodsImage"
|
||||
>
|
||||
<IconifyIconOffline class="upload-plus" :icon="Plus" />
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="附件">
|
||||
<el-upload
|
||||
multiple
|
||||
class="attachment-upload"
|
||||
:file-list="attachmentUploadFiles"
|
||||
:http-request="option => handleUpload(option, 'ATTACHMENT')"
|
||||
:on-preview="handlePreviewAttachment"
|
||||
:on-remove="handleRemoveAttachment"
|
||||
>
|
||||
<el-button>上传附件</el-button>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="拍摄要求">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
v-model="formData.requirements"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="备注">
|
||||
<el-input type="textarea" :rows="3" v-model="formData.remark" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="笔记任务">
|
||||
<el-button type="primary" plain @click="addTask">添加笔记</el-button>
|
||||
<div
|
||||
v-for="(item, index) in formData.tasks"
|
||||
:key="index"
|
||||
class="line-item"
|
||||
>
|
||||
<el-date-picker
|
||||
v-model="item.releaseDate"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="发布日期"
|
||||
/>
|
||||
<el-button type="danger" link @click="removeTask(index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="支出信息">
|
||||
<el-button type="primary" plain @click="addExpenditure"
|
||||
>添加支出</el-button
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in formData.expenditures"
|
||||
:key="index"
|
||||
class="line-item"
|
||||
>
|
||||
<el-date-picker
|
||||
v-model="item.spendDate"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="支出日期"
|
||||
/>
|
||||
<el-input-number
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
v-model="item.amount"
|
||||
placeholder="金额"
|
||||
/>
|
||||
<el-select v-model="item.purpose" placeholder="用途" clearable>
|
||||
<el-option
|
||||
v-for="option in optionMap.expenditurePurpose"
|
||||
:key="option"
|
||||
:label="option"
|
||||
:value="option"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="danger" link @click="removeExpenditure(index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="结款信息">
|
||||
<el-button type="primary" plain @click="addSettlement"
|
||||
>添加结款</el-button
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in formData.settlements"
|
||||
:key="index"
|
||||
class="line-item"
|
||||
>
|
||||
<el-date-picker
|
||||
v-model="item.settleDate"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="结款日期"
|
||||
/>
|
||||
<el-select v-model="item.method" placeholder="方式" clearable>
|
||||
<el-option
|
||||
v-for="option in optionMap.settlementMethod"
|
||||
:key="option"
|
||||
:label="option"
|
||||
:value="option"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input-number
|
||||
:min="0"
|
||||
controls-position="right"
|
||||
v-model="item.income"
|
||||
placeholder="金额"
|
||||
/>
|
||||
<el-select v-model="item.purpose" placeholder="用途" clearable>
|
||||
<el-option
|
||||
v-for="option in optionMap.settlementPurpose"
|
||||
:key="option"
|
||||
:label="option"
|
||||
:value="option"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="danger" link @click="removeSettlement(index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-form>
|
||||
<el-dialog v-model="isImagePreviewVisible" append-to-body>
|
||||
<img class="preview-image" :src="previewImageUrl" alt="" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.line-item {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.record-form {
|
||||
:deep(.el-row .el-select),
|
||||
:deep(.el-row .el-date-editor.el-input),
|
||||
:deep(.el-row .el-input-number) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
:deep(.el-input-number .el-input__inner) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.goods-image-upload {
|
||||
:deep(.el-upload--picture-card),
|
||||
:deep(.el-upload-list--picture-card .el-upload-list__item) {
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
line-height: 96px;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-plus {
|
||||
font-size: 24px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
|
||||
.attachment-upload {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.preview-image {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,241 @@
|
||||
import dayjs from "dayjs";
|
||||
import { message } from "@/utils/message";
|
||||
import { ElMessageBox, Sort } from "element-plus";
|
||||
import { computed, onMounted, reactive, ref, toRaw } from "vue";
|
||||
import { CommonUtils } from "@/utils/common";
|
||||
import { PaginationProps } from "@pureadmin/table";
|
||||
import {
|
||||
CollaborationOptionResponse,
|
||||
CollaborationRecordListCommand,
|
||||
CollaborationRecordPageResponse,
|
||||
deleteCollaborationRecordApi,
|
||||
getCollaborationOptionsApi,
|
||||
getCollaborationRecordListApi
|
||||
} from "@/api/collaboration/record";
|
||||
|
||||
const statusTypeMap = {
|
||||
NONE: "info",
|
||||
SETTLED: "success",
|
||||
UNSETTLED: "danger",
|
||||
PARTIAL: "warning"
|
||||
};
|
||||
|
||||
export function useCollaborationRecordHook() {
|
||||
const defaultSort: Sort = {
|
||||
prop: "deadline",
|
||||
order: "descending"
|
||||
};
|
||||
|
||||
const pagination: PaginationProps = {
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
currentPage: 1,
|
||||
background: true
|
||||
};
|
||||
|
||||
const searchFormParams = reactive<CollaborationRecordListCommand>({
|
||||
brand: "",
|
||||
goods: "",
|
||||
cooperationPlatform: undefined
|
||||
});
|
||||
|
||||
const deadlineRange = computed<[string, string] | null>({
|
||||
get: () => getRange(searchFormParams.beginTime, searchFormParams.endTime),
|
||||
set: v => fillRange(v, "beginTime", "endTime")
|
||||
});
|
||||
|
||||
const purchaseRange = computed<[string, string] | null>({
|
||||
get: () =>
|
||||
getRange(
|
||||
searchFormParams.purchaseBeginTime,
|
||||
searchFormParams.purchaseEndTime
|
||||
),
|
||||
set: v => fillRange(v, "purchaseBeginTime", "purchaseEndTime")
|
||||
});
|
||||
|
||||
const dataList = ref<CollaborationRecordPageResponse[]>([]);
|
||||
const optionMap = ref<Record<string, string[]>>({});
|
||||
const pageLoading = ref(true);
|
||||
const multipleSelection = ref<number[]>([]);
|
||||
const sortState = ref<Sort>(defaultSort);
|
||||
|
||||
const columns: TableColumnList = [
|
||||
{ type: "selection", align: "left" },
|
||||
{ label: "品牌", prop: "brand", minWidth: 120 },
|
||||
{ label: "物品", prop: "goods", minWidth: 120 },
|
||||
{ label: "合作平台", prop: "cooperationPlatform", minWidth: 110 },
|
||||
{ label: "留存方式", prop: "retainedMethod", minWidth: 100 },
|
||||
{ label: "购入方式", prop: "purchaseMethod", minWidth: 100 },
|
||||
{
|
||||
label: "预完成日期",
|
||||
prop: "deadline",
|
||||
minWidth: 130,
|
||||
sortable: "custom",
|
||||
formatter: ({ deadline }) => formatDate(deadline)
|
||||
},
|
||||
{
|
||||
label: "任务进度",
|
||||
minWidth: 130,
|
||||
cellRenderer: ({ row }) => `${row.completedTasksNum}/${row.tasksNum}`
|
||||
},
|
||||
{
|
||||
label: "拍单费用",
|
||||
minWidth: 110,
|
||||
cellRenderer: ({ row, props }) =>
|
||||
renderStatus(row.purchaseSettlementStatus, props.size)
|
||||
},
|
||||
{
|
||||
label: "快递费用",
|
||||
minWidth: 110,
|
||||
cellRenderer: ({ row, props }) =>
|
||||
renderStatus(row.deliverySettlementStatus, props.size)
|
||||
},
|
||||
{
|
||||
label: "稿费",
|
||||
minWidth: 110,
|
||||
cellRenderer: ({ row, props }) =>
|
||||
renderStatus(row.remunerationSettlementStatus, props.size)
|
||||
},
|
||||
{
|
||||
label: "创建时间",
|
||||
prop: "createTime",
|
||||
minWidth: 160,
|
||||
sortable: "custom",
|
||||
formatter: ({ createTime }) => formatDateTime(createTime)
|
||||
},
|
||||
{ label: "操作", fixed: "right", width: 140, slot: "operation" }
|
||||
];
|
||||
|
||||
function getRange(start?: string, end?: string) {
|
||||
if (!start || !end) return null;
|
||||
return [start, end] as [string, string];
|
||||
}
|
||||
|
||||
function fillRange(v, startKey: string, endKey: string) {
|
||||
searchFormParams[startKey] = v?.length === 2 ? v[0] : undefined;
|
||||
searchFormParams[endKey] = v?.length === 2 ? v[1] : undefined;
|
||||
}
|
||||
|
||||
function formatDate(value?: string) {
|
||||
return value ? dayjs(value).format("YYYY-MM-DD") : "";
|
||||
}
|
||||
|
||||
function formatDateTime(value?: string) {
|
||||
return value ? dayjs(value).format("YYYY-MM-DD HH:mm:ss") : "";
|
||||
}
|
||||
|
||||
function renderStatus(status, size) {
|
||||
if (!status) return "";
|
||||
return (
|
||||
<el-tag size={size} type={statusTypeMap[status.status]} effect="plain">
|
||||
{status.label}
|
||||
</el-tag>
|
||||
);
|
||||
}
|
||||
|
||||
function onSortChanged(sort: Sort) {
|
||||
sortState.value = sort;
|
||||
pagination.currentPage = 1;
|
||||
getRecordList();
|
||||
}
|
||||
|
||||
async function onSearch(tableRef) {
|
||||
tableRef.getTableRef().sort("deadline", "descending");
|
||||
}
|
||||
|
||||
function resetForm(formEl, tableRef) {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
fillRange(null, "beginTime", "endTime");
|
||||
fillRange(null, "purchaseBeginTime", "purchaseEndTime");
|
||||
onSearch(tableRef);
|
||||
}
|
||||
|
||||
async function getRecordList() {
|
||||
pageLoading.value = true;
|
||||
CommonUtils.fillSortParams(searchFormParams, sortState.value);
|
||||
CommonUtils.fillPaginationParams(searchFormParams, pagination);
|
||||
const { data } = await getCollaborationRecordListApi(
|
||||
toRaw(searchFormParams)
|
||||
).finally(() => {
|
||||
pageLoading.value = false;
|
||||
});
|
||||
dataList.value = data.rows;
|
||||
pagination.total = data.total;
|
||||
}
|
||||
|
||||
async function getOptions() {
|
||||
const { data } = await getCollaborationOptionsApi();
|
||||
optionMap.value = data.reduce(
|
||||
(result, item: CollaborationOptionResponse) => {
|
||||
result[item.type] = item.values;
|
||||
return result;
|
||||
},
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
async function handleDelete(row: CollaborationRecordPageResponse) {
|
||||
await deleteCollaborationRecordApi([row.recordId]);
|
||||
message(`您删除了编号为${row.recordId}的合作记录`, { type: "success" });
|
||||
getRecordList();
|
||||
}
|
||||
|
||||
async function handleBulkDelete(tableRef) {
|
||||
if (multipleSelection.value.length === 0) {
|
||||
message("请选择需要删除的数据", { type: "warning" });
|
||||
return;
|
||||
}
|
||||
confirmBulkDelete(tableRef);
|
||||
}
|
||||
|
||||
function confirmBulkDelete(tableRef) {
|
||||
ElMessageBox.confirm(
|
||||
`确认删除编号为[ ${multipleSelection.value} ]的合作记录吗?`,
|
||||
"系统提示",
|
||||
{
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
draggable: true
|
||||
}
|
||||
)
|
||||
.then(deleteSelectedRecords)
|
||||
.catch(() => {
|
||||
message("取消删除", { type: "info" });
|
||||
tableRef.getTableRef().clearSelection();
|
||||
});
|
||||
}
|
||||
|
||||
async function deleteSelectedRecords() {
|
||||
await deleteCollaborationRecordApi(multipleSelection.value);
|
||||
message(`您删除了编号为[ ${multipleSelection.value} ]的合作记录`, {
|
||||
type: "success"
|
||||
});
|
||||
getRecordList();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions();
|
||||
getRecordList();
|
||||
});
|
||||
|
||||
return {
|
||||
searchFormParams,
|
||||
pageLoading,
|
||||
columns,
|
||||
dataList,
|
||||
pagination,
|
||||
defaultSort,
|
||||
deadlineRange,
|
||||
purchaseRange,
|
||||
optionMap,
|
||||
multipleSelection,
|
||||
onSearch,
|
||||
onSortChanged,
|
||||
getRecordList,
|
||||
resetForm,
|
||||
handleDelete,
|
||||
handleBulkDelete
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<script setup lang="ts">
|
||||
import { useCollaborationStatisticsHook } from "./utils/hook";
|
||||
|
||||
defineOptions({
|
||||
name: "CollaborationStatistics"
|
||||
});
|
||||
|
||||
const { chartRef, selectedYear, yearOptions, getStatistics } =
|
||||
useCollaborationStatisticsHook();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main">
|
||||
<el-card shadow="never">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>月度统计</span>
|
||||
<el-select
|
||||
v-model="selectedYear"
|
||||
class="!w-[140px]"
|
||||
@change="getStatistics"
|
||||
>
|
||||
<el-option
|
||||
v-for="year in yearOptions"
|
||||
:key="year"
|
||||
:label="`${year}年`"
|
||||
:value="year"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
<div ref="chartRef" class="chart" />
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 600px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,84 @@
|
||||
import { onMounted, ref } from "vue";
|
||||
import * as echarts from "echarts";
|
||||
import { getCollaborationMonthlyStatisticsApi } from "@/api/collaboration/record";
|
||||
|
||||
export function useCollaborationStatisticsHook() {
|
||||
const currentYear = new Date().getFullYear();
|
||||
const selectedYear = ref(currentYear);
|
||||
const yearOptions = Array.from(
|
||||
{ length: currentYear - 2012 },
|
||||
(_, index) => currentYear - index
|
||||
);
|
||||
const chartRef = ref<HTMLElement>();
|
||||
let chart: echarts.ECharts | undefined;
|
||||
|
||||
async function getStatistics() {
|
||||
const { data } = await getCollaborationMonthlyStatisticsApi(
|
||||
selectedYear.value
|
||||
);
|
||||
renderChart(data);
|
||||
}
|
||||
|
||||
function renderChart(data) {
|
||||
chart?.dispose();
|
||||
chart = echarts.init(chartRef.value);
|
||||
chart.setOption({
|
||||
tooltip: { trigger: "axis", axisPointer: { type: "shadow" } },
|
||||
legend: {
|
||||
data: ["拍单费用", "支出费用", "已结稿费", "已结总费用"],
|
||||
top: "2%",
|
||||
right: "0"
|
||||
},
|
||||
grid: { left: "0%", right: "0%", bottom: "0%", containLabel: true },
|
||||
xAxis: { type: "category", data: data.map(item => `${item.month}月`) },
|
||||
yAxis: { type: "value" },
|
||||
series: [
|
||||
buildSeries(
|
||||
"拍单费用",
|
||||
data.map(item => item.purchasePrice),
|
||||
"#ffbe00"
|
||||
),
|
||||
buildSeries(
|
||||
"支出费用",
|
||||
data.map(item => item.expenditureAmount),
|
||||
"#f56c6c"
|
||||
),
|
||||
buildSeries(
|
||||
"已结稿费",
|
||||
data.map(item => item.settledRemuneration),
|
||||
"#409eff"
|
||||
),
|
||||
buildSeries(
|
||||
"已结总费用",
|
||||
data.map(item => item.settledTotal),
|
||||
"#67c23a"
|
||||
)
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function buildSeries(name: string, data: number[], color: string) {
|
||||
return {
|
||||
name,
|
||||
data,
|
||||
type: "bar",
|
||||
color
|
||||
};
|
||||
}
|
||||
|
||||
function resizeChart() {
|
||||
chart?.resize({ width: "auto" });
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getStatistics();
|
||||
window.addEventListener("resize", resizeChart);
|
||||
});
|
||||
|
||||
return {
|
||||
chartRef,
|
||||
selectedYear,
|
||||
yearOptions,
|
||||
getStatistics
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { h, ref } from "vue";
|
||||
import { usePostHook } from "./utils/hook";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
@@ -12,8 +13,15 @@ import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { CommonUtils } from "@/utils/common";
|
||||
import PostFormModal from "@/views/system/post/post-form-modal.vue";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
import { PostPageResponse } from "@/api/system/post";
|
||||
import {
|
||||
AddPostCommand,
|
||||
PostPageResponse,
|
||||
UpdatePostCommand,
|
||||
addPostApi,
|
||||
updatePostApi
|
||||
} from "@/api/system/post";
|
||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
/** 组件name最好和菜单表中的router_name一致 */
|
||||
defineOptions({
|
||||
@@ -43,13 +51,55 @@ const {
|
||||
handleBulkDelete
|
||||
} = usePostHook();
|
||||
|
||||
const opType = ref<"add" | "update">("add");
|
||||
const modalVisible = ref(false);
|
||||
const opRow = ref<PostPageResponse>();
|
||||
const postFormRef = ref();
|
||||
|
||||
function getPostFormData(row?: PostPageResponse) {
|
||||
return {
|
||||
postId: row?.postId ?? 0,
|
||||
postCode: row?.postCode ?? "",
|
||||
postName: row?.postName ?? "",
|
||||
postSort: row?.postSort ?? 1,
|
||||
remark: row?.remark ?? "",
|
||||
status: row?.status?.toString() ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
async function submitPostForm(
|
||||
type: "add" | "update",
|
||||
formData: AddPostCommand & Partial<UpdatePostCommand>,
|
||||
done: () => void
|
||||
) {
|
||||
if (type === "add") {
|
||||
await addPostApi(formData);
|
||||
} else {
|
||||
await updatePostApi(formData as UpdatePostCommand);
|
||||
}
|
||||
ElMessage.success("提交成功");
|
||||
done();
|
||||
onSearch(tableRef);
|
||||
}
|
||||
|
||||
function openDialog(type: "add" | "update", row?: PostPageResponse) {
|
||||
opType.value = type;
|
||||
opRow.value = row;
|
||||
modalVisible.value = true;
|
||||
const formInline = getPostFormData(row);
|
||||
addDialog({
|
||||
title: type === "add" ? "新增岗位" : "更新岗位",
|
||||
props: { formInline },
|
||||
width: "40%",
|
||||
draggable: true,
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(PostFormModal, { ref: postFormRef }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const formRuleRef = postFormRef.value.getFormRuleRef();
|
||||
const formData = options.props.formInline as AddPostCommand &
|
||||
Partial<UpdatePostCommand>;
|
||||
formRuleRef.validate(valid => {
|
||||
if (valid) {
|
||||
submitPostForm(type, formData, () => done());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -205,13 +255,6 @@ function openDialog(type: "add" | "update", row?: PostPageResponse) {
|
||||
</pure-table>
|
||||
</template>
|
||||
</PureTableBar>
|
||||
|
||||
<post-form-modal
|
||||
v-model="modalVisible"
|
||||
:type="opType"
|
||||
:row="opRow"
|
||||
@success="onSearch"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,43 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import VDialog from "@/components/VDialog/VDialog.vue";
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import {
|
||||
AddPostCommand,
|
||||
PostPageResponse,
|
||||
UpdatePostCommand,
|
||||
addPostApi,
|
||||
updatePostApi
|
||||
} from "@/api/system/post";
|
||||
import { ref } from "vue";
|
||||
import { AddPostCommand, UpdatePostCommand } from "@/api/system/post";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { ElMessage, FormInstance, FormRules } from "element-plus";
|
||||
import { FormInstance, FormRules } from "element-plus";
|
||||
|
||||
interface Props {
|
||||
type: "add" | "update";
|
||||
modelValue: boolean;
|
||||
row?: PostPageResponse;
|
||||
formInline: AddPostCommand & Partial<UpdatePostCommand>;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emits = defineEmits<{
|
||||
(e: "update:modelValue", v: boolean): void;
|
||||
(e: "success"): void;
|
||||
}>();
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set(v) {
|
||||
emits("update:modelValue", v);
|
||||
}
|
||||
});
|
||||
|
||||
const formData = reactive<AddPostCommand | UpdatePostCommand>({
|
||||
postId: 0,
|
||||
postCode: "",
|
||||
postName: "",
|
||||
postSort: 1,
|
||||
remark: "",
|
||||
status: ""
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
formInline: () => ({
|
||||
postId: 0,
|
||||
postCode: "",
|
||||
postName: "",
|
||||
postSort: 1,
|
||||
remark: "",
|
||||
status: ""
|
||||
})
|
||||
});
|
||||
const formData = ref(props.formInline);
|
||||
|
||||
const statusList = useUserStoreHook().dictionaryMap["common.status"];
|
||||
|
||||
@@ -62,70 +43,37 @@ const rules: FormRules = {
|
||||
]
|
||||
};
|
||||
const formRef = ref<FormInstance>();
|
||||
function handleOpened() {
|
||||
if (props.row) {
|
||||
Object.assign(formData, props.row);
|
||||
} else {
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
|
||||
function getFormRuleRef() {
|
||||
return formRef.value;
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
loading.value = true;
|
||||
if (props.type === "add") {
|
||||
await addPostApi(formData);
|
||||
} else if (props.type === "update") {
|
||||
await updatePostApi(formData as UpdatePostCommand);
|
||||
}
|
||||
ElMessage.info("提交成功");
|
||||
visible.value = false;
|
||||
emits("success");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
ElMessage.error((e as Error)?.message || "提交失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
defineExpose({ getFormRuleRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog
|
||||
show-full-screen
|
||||
:fixed-body-height="false"
|
||||
use-body-scrolling
|
||||
:title="type === 'add' ? '新增岗位' : '更新岗位'"
|
||||
v-model="visible"
|
||||
:loading="loading"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="visible = false"
|
||||
@opened="handleOpened"
|
||||
>
|
||||
<el-form :model="formData" label-width="120px" :rules="rules" ref="formRef">
|
||||
<el-form-item prop="postName" label="岗位名称" required inline-message>
|
||||
<el-input v-model="formData.postName" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="postCode" label="岗位编码" required>
|
||||
<el-input v-model="formData.postCode" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="postSort" label="岗位顺序" required>
|
||||
<el-input-number :min="1" v-model="formData.postSort" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="岗位状态">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="item in Object.keys(statusList)"
|
||||
:key="item"
|
||||
:label="statusList[item].value"
|
||||
>{{ statusList[item].label }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="备注" style="margin-bottom: 0">
|
||||
<el-input type="textarea" v-model="formData.remark" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</v-dialog>
|
||||
<el-form :model="formData" label-width="120px" :rules="rules" ref="formRef">
|
||||
<el-form-item prop="postName" label="岗位名称" required inline-message>
|
||||
<el-input v-model="formData.postName" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="postCode" label="岗位编码" required>
|
||||
<el-input v-model="formData.postCode" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="postSort" label="岗位顺序" required>
|
||||
<el-input-number :min="1" v-model="formData.postSort" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="岗位状态">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="item in Object.keys(statusList)"
|
||||
:key="item"
|
||||
:label="statusList[item].value"
|
||||
>{{ statusList[item].label }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="备注" style="margin-bottom: 0">
|
||||
<el-input type="textarea" v-model="formData.remark" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { h, ref } from "vue";
|
||||
import { useRole } from "./utils/hook";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||
import Search from "@iconify-icons/ep/search";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||
import { getRoleInfoApi, RoleDTO } from "@/api/system/role";
|
||||
import {
|
||||
AddRoleCommand,
|
||||
RoleDTO,
|
||||
UpdateRoleCommand,
|
||||
addRoleApi,
|
||||
getRoleInfoApi,
|
||||
updateRoleApi
|
||||
} from "@/api/system/role";
|
||||
import RoleFormModal from "@/views/system/role/role-form-modal.vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
@@ -31,11 +39,37 @@ const {
|
||||
handleDelete
|
||||
} = useRole();
|
||||
|
||||
const opType = ref<"add" | "update">("add");
|
||||
const modalVisible = ref(false);
|
||||
const opRow = ref<RoleDTO>();
|
||||
const roleFormRef = ref();
|
||||
|
||||
function getRoleFormData(row?: RoleDTO) {
|
||||
return {
|
||||
roleId: row?.roleId ?? 0,
|
||||
dataScope: row?.dataScope?.toString() ?? "",
|
||||
menuIds: row?.selectedMenuList ?? [],
|
||||
remark: row?.remark ?? "",
|
||||
roleKey: row?.roleKey ?? "",
|
||||
roleName: row?.roleName ?? "",
|
||||
roleSort: row?.roleSort ?? 1,
|
||||
status: row?.status?.toString() ?? ""
|
||||
};
|
||||
}
|
||||
|
||||
async function submitRoleForm(
|
||||
type: "add" | "update",
|
||||
formData: AddRoleCommand & Partial<UpdateRoleCommand>,
|
||||
done: () => void
|
||||
) {
|
||||
if (type === "add") {
|
||||
await addRoleApi(formData);
|
||||
} else {
|
||||
await updateRoleApi(formData as UpdateRoleCommand);
|
||||
}
|
||||
ElMessage.success("提交成功");
|
||||
done();
|
||||
onSearch();
|
||||
}
|
||||
|
||||
async function openDialog(type: "add" | "update", row?: RoleDTO) {
|
||||
debugger;
|
||||
try {
|
||||
await getMenuTree();
|
||||
if (row) {
|
||||
@@ -46,10 +80,43 @@ async function openDialog(type: "add" | "update", row?: RoleDTO) {
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
ElMessage.error((e as Error)?.message || "加载菜单失败");
|
||||
return;
|
||||
}
|
||||
opType.value = type;
|
||||
opRow.value = row;
|
||||
modalVisible.value = true;
|
||||
const formInline = getRoleFormData(row);
|
||||
addDialog({
|
||||
title: type === "add" ? "新增角色" : "更新角色",
|
||||
props: {
|
||||
formInline,
|
||||
menuOptions: menuTree.value
|
||||
},
|
||||
width: "40%",
|
||||
draggable: true,
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(RoleFormModal, { ref: roleFormRef }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const formRuleRef = roleFormRef.value.getFormRuleRef();
|
||||
const formData = options.props.formInline as AddRoleCommand &
|
||||
Partial<UpdateRoleCommand>;
|
||||
formRuleRef.validate(valid => {
|
||||
if (valid) {
|
||||
submitRoleForm(type, formData, () => done());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelectionChange(rows: RoleDTO[]) {
|
||||
void rows;
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
onSearch();
|
||||
}
|
||||
|
||||
function handleCurrentChange() {
|
||||
onSearch();
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
@@ -205,13 +272,6 @@ async function openDialog(type: "add" | "update", row?: RoleDTO) {
|
||||
</pure-table>
|
||||
</template>
|
||||
</PureTableBar>
|
||||
|
||||
<role-form-modal
|
||||
v-model="modalVisible"
|
||||
:type="opType"
|
||||
:row="opRow"
|
||||
:menu-options="menuTree"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,47 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import VDialog from "@/components/VDialog/VDialog.vue";
|
||||
import { computed, reactive, ref } from "vue";
|
||||
import { ref } from "vue";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { ElMessage, FormInstance, FormRules } from "element-plus";
|
||||
import {
|
||||
AddRoleCommand,
|
||||
RoleDTO,
|
||||
UpdateRoleCommand,
|
||||
addRoleApi,
|
||||
updateRoleApi
|
||||
} from "@/api/system/role";
|
||||
import { ElTree, FormInstance, FormRules } from "element-plus";
|
||||
import { AddRoleCommand, UpdateRoleCommand } from "@/api/system/role";
|
||||
import { MenuDTO } from "@/api/system/menu";
|
||||
|
||||
interface Props {
|
||||
type: "add" | "update";
|
||||
modelValue: boolean;
|
||||
row?: RoleDTO;
|
||||
formInline: AddRoleCommand & Partial<UpdateRoleCommand>;
|
||||
menuOptions: MenuDTO[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emits = defineEmits<{
|
||||
(e: "update:modelValue", v: boolean): void;
|
||||
(e: "success"): void;
|
||||
}>();
|
||||
|
||||
const visible = computed({
|
||||
get: () => props.modelValue,
|
||||
set(v) {
|
||||
emits("update:modelValue", v);
|
||||
}
|
||||
});
|
||||
|
||||
const formData = reactive<AddRoleCommand | UpdateRoleCommand>({
|
||||
roleId: 0,
|
||||
dataScope: "",
|
||||
menuIds: [],
|
||||
remark: "",
|
||||
roleKey: "",
|
||||
roleName: "",
|
||||
roleSort: 1,
|
||||
status: ""
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
formInline: () => ({
|
||||
roleId: 0,
|
||||
dataScope: "",
|
||||
menuIds: [],
|
||||
remark: "",
|
||||
roleKey: "",
|
||||
roleName: "",
|
||||
roleSort: 1,
|
||||
status: ""
|
||||
}),
|
||||
menuOptions: () => []
|
||||
});
|
||||
const formData = ref(props.formInline);
|
||||
|
||||
const statusList = useUserStoreHook().dictionaryMap["common.status"];
|
||||
|
||||
@@ -66,93 +48,58 @@ const rules: FormRules = {
|
||||
]
|
||||
};
|
||||
const formRef = ref<FormInstance>();
|
||||
function handleOpened() {
|
||||
console.log("opened", props.row);
|
||||
if (props.row) {
|
||||
Object.assign(formData, props.row);
|
||||
formData.menuIds = props.row.selectedMenuList;
|
||||
} else {
|
||||
formRef.value?.resetFields();
|
||||
}
|
||||
}
|
||||
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>();
|
||||
function handleCheckChange() {
|
||||
formData.menuIds = treeRef.value.getCheckedKeys(false) as number[];
|
||||
formData.value.menuIds = treeRef.value.getCheckedKeys(false) as number[];
|
||||
}
|
||||
|
||||
const loading = ref(false);
|
||||
async function handleConfirm() {
|
||||
try {
|
||||
loading.value = true;
|
||||
if (props.type === "add") {
|
||||
await addRoleApi(formData);
|
||||
} else if (props.type === "update") {
|
||||
await updateRoleApi(formData as UpdateRoleCommand);
|
||||
}
|
||||
ElMessage.info("提交成功");
|
||||
visible.value = false;
|
||||
emits("success");
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
ElMessage.error((e as Error)?.message || "提交失败");
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
function getFormRuleRef() {
|
||||
return formRef.value;
|
||||
}
|
||||
|
||||
defineExpose({ getFormRuleRef });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog
|
||||
show-full-screen
|
||||
fixed-body-height
|
||||
use-body-scrolling
|
||||
:title="type === 'add' ? '新增角色' : '更新角色'"
|
||||
v-model="visible"
|
||||
:loading="loading"
|
||||
@confirm="handleConfirm"
|
||||
@cancel="visible = false"
|
||||
@opened="handleOpened"
|
||||
>
|
||||
<el-form :model="formData" label-width="120px" :rules="rules" ref="formRef">
|
||||
<el-form-item prop="roleName" label="角色名称" required inline-message>
|
||||
<el-input v-model="formData.roleName" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="roleKey" label="权限字符" required>
|
||||
<el-input v-model="formData.roleKey" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="roleSort" label="角色顺序" required>
|
||||
<el-input-number :min="1" v-model="formData.roleSort" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="角色状态">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="item in Object.keys(statusList)"
|
||||
:key="item"
|
||||
:label="statusList[item].value"
|
||||
>{{ statusList[item].label }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单权限" prop="menuIds">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:props="{ label: 'menuName', children: 'children' }"
|
||||
:data="props.menuOptions"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
check-on-click-node
|
||||
:expand-on-click-node="false"
|
||||
:default-checked-keys="formData.menuIds"
|
||||
@check-change="handleCheckChange"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="备注" style="margin-bottom: 0">
|
||||
<el-input type="textarea" v-model="formData.remark" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</v-dialog>
|
||||
<el-form :model="formData" label-width="120px" :rules="rules" ref="formRef">
|
||||
<el-form-item prop="roleName" label="角色名称" required inline-message>
|
||||
<el-input v-model="formData.roleName" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="roleKey" label="权限字符" required>
|
||||
<el-input v-model="formData.roleKey" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="roleSort" label="角色顺序" required>
|
||||
<el-input-number :min="1" v-model="formData.roleSort" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="status" label="角色状态">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
v-for="item in Object.keys(statusList)"
|
||||
:key="item"
|
||||
:label="statusList[item].value"
|
||||
>{{ statusList[item].label }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="菜单权限" prop="menuIds">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:props="{ label: 'menuName', children: 'children' }"
|
||||
:data="props.menuOptions"
|
||||
node-key="id"
|
||||
check-strictly
|
||||
show-checkbox
|
||||
default-expand-all
|
||||
check-on-click-node
|
||||
:expand-on-click-node="false"
|
||||
:default-checked-keys="formData.menuIds"
|
||||
@check-change="handleCheckChange"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="备注" style="margin-bottom: 0">
|
||||
<el-input type="textarea" v-model="formData.remark" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user