Browse Source

feat: 0513 需求开发

master
AaronWu 3 weeks ago
parent
commit
75a00cc181
  1. 2
      .env.development
  2. 4
      components.d.ts
  3. 15
      src/App.vue
  4. 1
      src/api/login/model.d.ts
  5. 2
      src/api/user/model.d.ts
  6. 1
      src/components/core/schema-form/src/hooks/useFormEvents.ts
  7. 3
      src/utils/common.ts
  8. 9
      src/views/client/entrance/index.vue
  9. 82
      src/views/client/issue/index.vue
  10. 23
      src/views/login/index.vue
  11. 19
      src/views/question/issue/columns.tsx
  12. 118
      src/views/question/issue/detail.vue
  13. 6
      src/views/question/issue/formSchemas.tsx
  14. 10
      src/views/system/user/columns.tsx
  15. 8
      src/views/system/user/formSchemas.ts

2
.env.development

@ -9,7 +9,7 @@
# 只在开发模式中被载入
# 网站前缀
# VITE_BASE_API_URL = http://192.168.2.33:8089/server/
# VITE_BASE_API_URL = http://192.168.2.3:8089/server/
VITE_BASE_API_URL = http://43.137.2.78:8085/server/
# base api

4
components.d.ts

@ -24,6 +24,9 @@ declare module '@vue/runtime-core' {
ALayout: typeof import('ant-design-vue/es')['Layout']
ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider']
AList: typeof import('ant-design-vue/es')['List']
AListItem: typeof import('ant-design-vue/es')['ListItem']
AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta']
AModal: typeof import('ant-design-vue/es')['Modal']
ApiSelect: typeof import('./src/components/core/schema-form/src/components/ApiSelect.vue')['default']
ARow: typeof import('ant-design-vue/es')['Row']
@ -34,6 +37,7 @@ declare module '@vue/runtime-core' {
ATabPane: typeof import('ant-design-vue/es')['TabPane']
ATabs: typeof import('ant-design-vue/es')['Tabs']
ATag: typeof import('ant-design-vue/es')['Tag']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ATree: typeof import('ant-design-vue/es')['Tree']
ATypographyText: typeof import('ant-design-vue/es')['TypographyText']

15
src/App.vue

@ -17,7 +17,6 @@
const { getAntdLocale } = useLocale();
watchEffect(() => {
if (route.meta?.title) {
//
document.title = transformI18n(route.meta.title);
@ -31,8 +30,18 @@
align-items: center;
}
.anticon svg{
.anticon svg {
display: block !important;
}
.ant-input[disabled] {
color: #333333 !important;
}
.ant-select-disabled.ant-select:not(.ant-select-customize-input) .ant-select-selector {
color: #333333 !important;
}
.ant-select-disabled.ant-select-multiple .ant-select-selection-item {
color: #333333 !important;
}
</style>

1
src/api/login/model.d.ts

@ -37,6 +37,7 @@ declare namespace API {
password: string;
account: string;
sex?: number;
company?: string;
};
/** 登录成功结果 */

2
src/api/user/model.d.ts

@ -18,6 +18,7 @@ declare namespace API {
state: number;
remark: string;
createTime: string;
company: string;
isAdmin: number;
email: string;
auditState?: number;
@ -33,6 +34,7 @@ declare namespace API {
state: number;
remark: string;
isAdmin: number;
company: string;
email: string;
};

1
src/components/core/schema-form/src/hooks/useFormEvents.ts

@ -204,7 +204,6 @@ export function useFormEvents(formActionContext: UseFormActionContext) {
}
});
});
unref(formPropsRef).schemas = uniqBy(schemas, 'field');
};

3
src/utils/common.ts

@ -1,4 +1,5 @@
import dayjs from 'dayjs';
import { message } from 'ant-design-vue';
/**
* @description abc => Abc
@ -39,6 +40,8 @@ export const copyText = (text: string) => {
copyInput.select();
document.execCommand('copy');
copyInput.remove();
// 添加提示
message.success('复制成功');
resolve(true);
});
};

9
src/views/client/entrance/index.vue

@ -8,7 +8,7 @@
</div>
<div class="nav-links">
<a-space>
<a class="nav-item" @click="goTo('/question/issue')">返回后台</a>
<a class="nav-item" v-if="userInfo.isAdmin === 1" @click="goTo('/question/issue')">返回后台</a>
<!-- <a href="#" class="nav-item">问题社区</a> -->
<!-- <a href="#" class="nav-item">更新日志</a> -->
<!-- <a-button type="text" class="favorite">
@ -73,6 +73,10 @@
<a-descriptions-item label="邮箱">
<mail-outlined class="info-icon" /> {{ userInfo.email || '未设置' }}
</a-descriptions-item>
<!-- 公司 company -->
<a-descriptions-item label="公司">
<UsergroupDeleteOutlined class="info-icon" /> {{ userInfo.company || '未设置' }}
</a-descriptions-item>
<a-descriptions-item label="注册时间">
<calendar-outlined class="info-icon" /> {{ userInfo.createTime }}
</a-descriptions-item>
@ -222,6 +226,7 @@
ProfileOutlined,
MobileOutlined,
CalendarOutlined,
UsergroupDeleteOutlined,
} from '@ant-design/icons-vue';
import { useRouter, useRoute } from 'vue-router';
import { Avatar, Menu, Dropdown, Modal, message } from 'ant-design-vue';
@ -247,7 +252,7 @@
const startItems = ref([
{
title: '问题工单',
title: '我的工单',
description: '提交问题和请求,跟踪进度',
icon: FormOutlined,
route: '/issue',

82
src/views/client/issue/index.vue

@ -9,14 +9,14 @@
</div>
<a-row :gutter="16">
<!-- 左侧目录树 -->
<a-col :span="4">
<a-col :span="6">
<a-card class="tree-card">
<template #title>
<span class="card-title"> <folder-outlined /> 问题工单目录 </span>
<span class="card-title"> <folder-outlined /> 我提交的工单 </span>
</template>
<a-spin :spinning="loading">
<!-- :defaultExpandAll="expandAll" -->
<a-tree
<!-- <a-tree
v-if="expandAll"
v-model:selectedKeys="selectedKeys"
:tree-data="treeData"
@ -31,19 +31,34 @@
<a-tag v-if="item.isLeaf" :color="item.color">{{ item.stateText }}</a-tag></div
>
</template>
<!-- <template #icon="item">
<template v-if="item.isLeaf">
<a-tag :color="item.color">{{ item.stateText }}</a-tag>
</a-tree> -->
<a-list item-layout="horizontal" :data-source="treeData">
<template #renderItem="{ item }">
<a-list-item
:key="item.key"
:style="{
cursor: 'pointer',
backgroundColor: curRow.id === item.key ? '#e6f7ff' : 'transparent', //
}"
@click="handleClick(item.key)"
>
<a-list-item-meta :description="item.description">
<template #title>
<div class="w-full truncate" :title="item.title">{{ item.title }}</div>
</template>
<template #avatar>
<a-tag v-if="item.isLeaf" :color="item.color">{{ item.stateText }}</a-tag>
</template>
</a-list-item-meta>
</a-list-item>
</template>
</template> -->
</a-tree>
</a-list>
</a-spin>
</a-card>
</a-col>
<!-- 右侧内容区 -->
<a-col :span="20">
<a-col :span="18">
<a-card class="content-card">
<template #title>
<div class="flex justify-between items-start">
@ -273,10 +288,12 @@
//
if (values.id) {
//
await initTreeData();
// await initTreeData();
await initListData();
detailRef.value?.initData();
} else {
initTreeData();
// initTreeData();
await initListData();
}
}
};
@ -373,6 +390,12 @@
}
};
const handleClick = async (id: string) => {
curRow.value = {
id,
};
};
const initTreeData = async () => {
let tree: any[] = [];
loading.value = true;
@ -443,7 +466,40 @@
console.log('treeData.value: ', treeData.value);
};
initTreeData();
// initTreeData();
const initListData = async () => {
let tree: any[] = [];
loading.value = true;
const {
data: { records },
} = await fetchMyIssuePageList({
current: 1,
size: 999,
});
if (records && Array.isArray(records) && records.length) {
tree = (records as any).map((e) => {
const { label, color } = stateTypeList.find((d) => d.value === e.state);
return {
title: e.title,
key: e.id,
id: e.id,
isLeaf: true,
state: e.state,
stateText: label,
color: color,
};
});
} else {
tree = [];
}
treeData.value = [...tree, ...[]];
expandAll.value = true;
loading.value = false;
};
initListData();
</script>
<style lang="less">

23
src/views/login/index.vue

@ -37,6 +37,17 @@
<template #prefix><safety-outlined /></template>
</a-input>
</a-form-item>
<!-- 公司 -->
<a-form-item v-if="state.isRegister">
<a-input
v-model:value="state.formInline.company"
size="large"
placeholder="请输入公司名称"
class="custom-input"
>
<template #prefix><UsergroupDeleteOutlined /></template>
</a-input>
</a-form-item>
<a-form-item>
<a-input
v-model:value="state.formInline.password"
@ -89,7 +100,12 @@
<script setup lang="ts">
import { reactive } from 'vue';
import { UserOutlined, LockOutlined, SafetyOutlined } from '@ant-design/icons-vue';
import {
UserOutlined,
LockOutlined,
SafetyOutlined,
UsergroupDeleteOutlined,
} from '@ant-design/icons-vue';
import { useRoute, useRouter } from 'vue-router';
import { message, Modal } from 'ant-design-vue';
import { useUserStore } from '@/store/modules/user';
@ -102,6 +118,7 @@
formInline: {
username: '',
password: '',
company: '', //
email: '', //
confirmPassword: '', //
},
@ -119,6 +136,7 @@
state.formInline.password = '';
state.formInline.confirmPassword = '';
state.formInline.email = '';
state.formInline.company = '';
};
//
@ -139,6 +157,9 @@
if (!/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(state.formInline.email)) {
return message.warning('请输入有效的邮箱地址!');
}
if (state.formInline.company.trim() === '') {
return message.warning('公司名称不能为空!');
}
// TODO:
state.loading = true;
try {

19
src/views/question/issue/columns.tsx

@ -14,7 +14,7 @@ export type TableColumnItem = TableColumn<TableListItem>;
export const baseColumns: TableColumnItem[] = [
{
title: '问题标题',
align: 'center',
align: 'left',
dataIndex: 'title',
// sorter: true,
width: 150,
@ -29,7 +29,7 @@ export const baseColumns: TableColumnItem[] = [
},
{
title: '问题描述',
align: 'center',
align: 'left',
dataIndex: 'description',
width: 150,
ellipsis: true,
@ -157,6 +157,21 @@ export const baseColumns: TableColumnItem[] = [
},
},
},
// 创建人 createUserName
{
title: '创建人',
align: 'center',
dataIndex: 'createUserName',
width: 150,
hideInSearch: true,
formItemProps: {
defaultValue: '',
required: false,
colProps: {
span: 6,
},
},
},
// state 问题状态
{
title: '问题状态',

118
src/views/question/issue/detail.vue

@ -56,6 +56,26 @@
/>
</div>
</template>
<template #description="{ formModel, field }">
<a-row class="flex items-center h-full" gutter="{8}">
<a-col :span="23">
<a-textarea
v-model:value="formModel[field]"
showCount
disabled
:maxLength="200"
placeholder="请输入问题描述"
/>
</a-col>
<a-col :span="1">
<div
class="flex items-center justify-center h-full cursor-pointer h-[54px] w-full border-[1px] border-solid border-[#d9d9d9]"
>
<CopyOutlined @click="handleCopyDescription(formModel[field])"></CopyOutlined>
</div>
</a-col>
</a-row>
</template>
</SchemaForm>
</a-tab-pane>
<a-tab-pane key="2" tab="处理历史">
@ -73,17 +93,19 @@
</div>
</template>
<script lang="ts" setup>
<script lang="tsx" setup>
import { QuillEditor } from '@vueup/vue-quill';
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import { useForm } from '@/components/core/schema-form';
import { getEditFormSchema } from './formSchemas';
import { useRoute } from 'vue-router';
import { onMounted, ref, watch } from 'vue';
import { onMounted, ref, watch, nextTick } from 'vue';
import { findOneById } from '@/api/issue';
import { SvgIcon } from '@/components/basic/svg-icon';
import { stateTypeList } from './data';
import { fetchVersionPageList } from '@/api/prodVersion';
import { CopyOutlined } from '@ant-design/icons-vue';
import { copyText } from '@/utils/common';
const props = defineProps({
id: {
@ -129,6 +151,17 @@
disabled: true,
});
let fields = ['billcode', 'contacts', 'title', 'description'];
let contactsText = ref(),
billcodeText = ref(),
titleText = ref(),
descriptionText = ref();
const handleCopyDescription = (text) => {
copyText(text);
};
const initData = async () => {
// route.query.idprops.id
const id = props.id || route.query.id;
@ -182,6 +215,87 @@
}
}
formRef?.updateSchema({
field: 'description',
slot: 'description',
});
contactsText.value = res?.contacts;
billcodeText.value = res?.billcode;
titleText.value = res?.title;
descriptionText.value = res?.description;
formRef?.updateSchema({
field: 'contacts',
componentProps: {
addonAfter: (
<CopyOutlined
onClick={() => {
console.log('contactsText.value: ', contactsText.value);
copyText(contactsText.value);
}}
/>
),
},
});
formRef?.updateSchema({
field: 'billcode',
componentProps: {
addonAfter: (
<CopyOutlined
onClick={() => {
console.log('billcodeText.value: ', billcodeText.value);
copyText(billcodeText.value);
}}
/>
),
},
});
formRef?.updateSchema({
field: 'title',
componentProps: {
addonAfter: (
<CopyOutlined
onClick={() => {
console.log('titleText.value: ', titleText.value);
copyText(titleText.value);
}}
/>
),
},
});
formRef?.updateSchema({
field: 'description',
componentProps: {
addonAfter: (
<CopyOutlined
onClick={() => {
console.log('descriptionText.value: ', descriptionText.value);
copyText(descriptionText.value);
}}
/>
),
},
});
// fields.forEach((item) => {
// console.log('res[item]: ', res[item]);
// text.value = res[item];
// console.log('text: ', text);
// let handleClick = (text) => {
// // copyText(text);
// return () => {
// copyText(text.value);
// };
// };
// formRef?.updateSchema({
// field: item,
// componentProps: {
// addonAfter: <CopyOutlined onClick={handleClick(text)} />,
// },
// });
// });
current.value = res?.state;
formRef?.setFieldsValue(res);

6
src/views/question/issue/formSchemas.tsx

@ -3,7 +3,7 @@ import { TableListItem } from './columns';
import { commonUpload } from '@/api/upload';
import { message, Button } from 'ant-design-vue';
import { getFileExtension } from '@/utils/fileUtils';
import { UploadOutlined } from '@ant-design/icons-vue';
import { UploadOutlined, CopyOutlined } from '@ant-design/icons-vue';
import { stateTypeList } from './data';
import { DictEnum } from '@/enums/dictEnum';
import { getDictionaryByTypeName } from '@/utils/dict';
@ -111,6 +111,7 @@ export const getEditFormSchema: (
colProps: {
span: 6,
},
rules: [{ required: true, type: 'string' }],
},
{
field: 'productId',
@ -177,6 +178,7 @@ export const getEditFormSchema: (
colProps: {
span: 6,
},
rules: [{ required: true, type: 'string' }],
},
{
@ -186,6 +188,7 @@ export const getEditFormSchema: (
colProps: {
span: 6,
},
rules: [{ required: true, type: 'string' }],
},
{
@ -237,6 +240,7 @@ export const getEditFormSchema: (
{
field: 'description',
// slot: 'description',
component: 'InputTextArea',
componentProps: {
rows: 4,

10
src/views/system/user/columns.tsx

@ -54,6 +54,16 @@ export const baseColumns: TableColumnItem[] = [
required: false,
},
},
{
title: '公司',
align: 'center',
dataIndex: 'company',
width: 150,
formItemProps: {
defaultValue: '',
required: false,
},
},
{
title: '性别',
align: 'center',

8
src/views/system/user/formSchemas.ts

@ -55,6 +55,14 @@ export const userSchemas: FormSchema<API.CreateUserParams>[] = [
},
],
},
{
field: 'company',
component: 'Input',
label: '公司',
colProps: {
span: 12,
},
},
{
field: 'mobile',
component: 'Input',

Loading…
Cancel
Save