1204 lines
36 KiB
Vue
1204 lines
36 KiB
Vue
<!-- 用户编辑弹窗 -->
|
||
<template>
|
||
<ele-modal
|
||
width="80%"
|
||
:visible="visible"
|
||
:maskClosable="false"
|
||
:maxable="maxable"
|
||
:title="isUpdate ? '编辑应用' : '添加应用'"
|
||
:body-style="{ paddingBottom: '28px' }"
|
||
@update:visible="updateVisible"
|
||
@ok="save"
|
||
>
|
||
<div style="background-color: #f3f3f3; padding: 8px">
|
||
<a-form
|
||
ref="formRef"
|
||
:model="form"
|
||
:rules="rules"
|
||
:label-col="{ md: { span: 7 }, sm: { span: 4 }, xs: { span: 24 } }"
|
||
:wrapper-col="{ md: { span: 17 }, sm: { span: 20 }, xs: { span: 24 } }"
|
||
>
|
||
<a-card title="基本信息" :bordered="false">
|
||
<a-row :gutter="16">
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<div class="upload-image">
|
||
<ele-image-upload
|
||
v-model:value="logo"
|
||
:item-style="{ width: '120px', height: '120px' }"
|
||
:limit="1"
|
||
@upload="onUploadIcon"
|
||
/>
|
||
</div>
|
||
<a-form-item label="AppId" name="appId">
|
||
<span>{{ form.appId }}</span>
|
||
</a-form-item>
|
||
<a-form-item label="AppSecret" name="appSecret">
|
||
<span style="margin-right: 10px;">{{ showAppSecret ? form.appSecret : '********' }}</span>
|
||
<a-space>
|
||
<a @click="onAppSecret(data.appId)">查看</a>
|
||
<a-divider type="vertical" />
|
||
<a @click="onUpdateAppSecret(data.appId)">重置</a>
|
||
</a-space>
|
||
</a-form-item>
|
||
<a-form-item label="企业ID" name="tenantId">
|
||
<span>{{ form.tenantId }}</span>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="应用名称" name="appName">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入应用名称"
|
||
v-model:value="form.appName"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="业主单位" name="companyName">
|
||
<SelectCompany
|
||
:placeholder="`请选择业主单位`"
|
||
v-model:value="form.companyName"
|
||
@done="chooseCompanyName"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="开发者单位" name="developer">
|
||
<SelectCompany
|
||
:placeholder="`请选择开发者企业`"
|
||
v-model:value="form.developer"
|
||
@done="chooseDeveloper"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="应用地址" name="appUrl">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入应用地址"
|
||
v-model:value="form.appUrl"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="后台管理" name="adminUrl">
|
||
<DictSelect
|
||
dict-code="AdminUrl"
|
||
placeholder="请选择后台管理地址"
|
||
v-model:value="form.adminUrl"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="应用状态" name="appStatus">
|
||
<DictSelect
|
||
dict-code="appstoreStatus"
|
||
placeholder="请选择应用状态"
|
||
v-model:value="form.appStatus"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="应用类型" name="appTypeMultiple">
|
||
<DictSelect
|
||
dict-code="AppTypeMultiple"
|
||
placeholder="请选择应用类型"
|
||
multiple="multiple"
|
||
v-model:value="form.appTypeMultiple"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="应用描述" name="comments">
|
||
<a-textarea
|
||
:rows="4"
|
||
:maxlength="200"
|
||
placeholder="请输入应用描述"
|
||
v-model:value="form.comments"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="开发设置" :bordered="false">
|
||
<a-row :gutter="16">
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="应用标识" name="appCode">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入应用标识(英文字母)"
|
||
v-model:value="form.appCode"
|
||
@change="changeAppCode"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="应用包名" name="packageName">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入应用包名"
|
||
v-model:value="form.packageName"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="IP白名单" name="ipAddress">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入IP白名单"
|
||
v-model:value="form.ipAddress"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="接口地址" name="serverUrl">
|
||
<DictSelect
|
||
dict-code="ServerApi"
|
||
placeholder="请选择管理地址"
|
||
v-model:value="form.serverUrl"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="文件服务器" name="fileUrl">
|
||
<DictSelect
|
||
dict-code="FileUrl"
|
||
placeholder="请选择文件服务器地址"
|
||
v-model:value="form.fileUrl"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="授权回调地址" name="callbackUrl">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入授权回调地址"
|
||
v-model:value="form.callbackUrl"
|
||
/>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="代码仓库" name="gitUrl">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入git仓库地址"
|
||
v-model:value="form.gitUrl"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="允许被搜索" name="gitUrl">
|
||
<a-switch v-model:checked="form.search" />
|
||
</a-form-item>
|
||
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="成员管理" :bordered="false">
|
||
<div class="content">
|
||
<a-space
|
||
style="margin-bottom: 15px"
|
||
v-if="isUpdate"
|
||
>
|
||
<SelectUser
|
||
:placeholder="`添加开发成员`"
|
||
@done="addAppDevUser"
|
||
/>
|
||
<SelectUser
|
||
:placeholder="`添加体验成员`"
|
||
@done="addAppExpUser"
|
||
/>
|
||
</a-space>
|
||
<table
|
||
class="ele-table ele-table-border ele-table-stripe ele-table-medium"
|
||
>
|
||
<thead>
|
||
<tr>
|
||
<th>角色</th>
|
||
<th>用户</th>
|
||
<th>邮箱</th>
|
||
<th>手机号码</th>
|
||
<th>加入时间</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="user in appUsers">
|
||
<td>
|
||
<a-tag color="green" v-if="user.role === 10">体验成员</a-tag>
|
||
<a-tag color="orange" v-if="user.role === 20">开发成员</a-tag>
|
||
<a-tag color="red" v-if="user.role === 30">管理员</a-tag>
|
||
</td>
|
||
<td>
|
||
<a-avatar :src="user.avatar">
|
||
<template #icon>
|
||
<user-outlined />
|
||
</template>
|
||
</a-avatar>
|
||
<span style="padding-left: 5px">{{ user.nickname }}</span>
|
||
</td>
|
||
<td>{{ user.email }}</td>
|
||
<td>{{ user.phone }}</td>
|
||
<td>{{ user.createTime }}</td>
|
||
<td>
|
||
<div v-if="user.role !== 30">
|
||
<a
|
||
@click="removeUser(user)"
|
||
v-if="user.status === 0"
|
||
>
|
||
移除
|
||
</a>
|
||
<a v-else>待确认</a>
|
||
</div>
|
||
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</a-card>
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="项目需求" :bordered="false">
|
||
<a-row :gutter="16">
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="原型图地址" name="prototypeUrl">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入原型图地址"
|
||
v-model:value="form.prototypeUrl"
|
||
/>
|
||
</a-form-item>
|
||
<a-form-item label="需求文档" name="parentId">
|
||
<a-upload
|
||
v-model:file-list="file3"
|
||
name="file"
|
||
action="https://file.wsdns.cn/api/file/image"
|
||
:headers="{ Authorization: token }"
|
||
@change="onFile3"
|
||
>
|
||
<a-button>
|
||
<upload-outlined />
|
||
上传
|
||
</a-button>
|
||
</a-upload>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="项目需求文档" name="docsUrl">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入腾讯文档地址"
|
||
v-model:value="form.docsUrl"
|
||
/>
|
||
</a-form-item>
|
||
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="维护文档" name="docsUrl">
|
||
<a-input
|
||
allow-clear
|
||
placeholder="请输入腾讯文档地址"
|
||
v-model:value="form.docsUrl"
|
||
/>
|
||
</a-form-item>
|
||
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="开发资料" :bordered="false">
|
||
<template #extra><a-button type="link" @click="handleEditStatus">{{ editStatus ? '预览' : '编辑'}}</a-button></template>
|
||
<!-- 编辑器 -->
|
||
<byte-md-editor
|
||
v-if="editStatus"
|
||
v-model:value="requirement"
|
||
placeholder="请输入您的内容,图片请直接粘贴"
|
||
:locale="zh_Hans"
|
||
mode="split"
|
||
:plugins="plugins"
|
||
height="500px"
|
||
:editorConfig="{ lineNumbers: true }"
|
||
@paste="onPaste"
|
||
/>
|
||
<!-- 编辑器 -->
|
||
<byte-md-viewer v-else :value="requirement" :plugins="plugins" />
|
||
</a-card>
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="工单信息" :bordered="false">
|
||
<table
|
||
class="ele-table ele-table-border ele-table-stripe ele-table-medium"
|
||
>
|
||
<thead>
|
||
<tr>
|
||
<th>工单号</th>
|
||
<th>工单标题</th>
|
||
<th>工单类型</th>
|
||
<th>工单状态</th>
|
||
<th>最后回复时间</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="user in tasks">
|
||
<td>
|
||
1766
|
||
</td>
|
||
<td>
|
||
发现一个bug,请帮忙核实并修复
|
||
</td>
|
||
<td>维护工单</td>
|
||
<td>待处理</td>
|
||
<td>{{ user.createTime }}</td>
|
||
<td>
|
||
<div>
|
||
<a>
|
||
去处理
|
||
</a>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</a-card>
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="应用截图" :bordered="false">
|
||
<!-- <template #extra><a-button type="link" @click="handleEditStatus">上传</a-button></template>-->
|
||
<div class="content">
|
||
<ele-image-upload
|
||
v-model:value="images"
|
||
:limit="6"
|
||
:drag="true"
|
||
:item-style="{ width: '150px', height: '267px' }"
|
||
:upload-handler="uploadHandler"
|
||
@upload="onUpload"
|
||
/>
|
||
<small class="ele-text-placeholder">
|
||
请上传应用截图(最多6张),建议宽度300*533像素,小于2M/张
|
||
</small>
|
||
</div>
|
||
</a-card>
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="应用介绍" :bordered="false">
|
||
<template #extra><a-button type="link" @click="handleEditStatus">{{ editStatus ? '预览' : '编辑'}}</a-button></template>
|
||
<!-- 编辑器 -->
|
||
<byte-md-editor
|
||
v-if="editStatus"
|
||
v-model:value="content"
|
||
placeholder="请输入您的内容,图片请直接粘贴"
|
||
:locale="zh_Hans"
|
||
mode="split"
|
||
:plugins="plugins"
|
||
height="500px"
|
||
:editorConfig="{ lineNumbers: true }"
|
||
@paste="onPaste"
|
||
/>
|
||
<!-- 编辑器 -->
|
||
<byte-md-viewer v-else :value="content" :plugins="plugins" />
|
||
</a-card>
|
||
<!-- <a-divider style="height: 8px" />-->
|
||
<!-- <a-card title="订单信息" :bordered="false" v-any-role="['superAdmin','admin']">-->
|
||
<!-- <table-->
|
||
<!-- class="ele-table ele-table-border ele-table-stripe ele-table-medium"-->
|
||
<!-- >-->
|
||
<!-- <thead>-->
|
||
<!-- <tr>-->
|
||
<!-- <th>订单号</th>-->
|
||
<!-- <th>订单类型</th>-->
|
||
<!-- <th>订单金额</th>-->
|
||
<!-- <th>支付状态</th>-->
|
||
<!-- <th>创建时间</th>-->
|
||
<!-- </tr>-->
|
||
<!-- </thead>-->
|
||
<!-- <tbody>-->
|
||
<!-- <tr v-for="user in appUsers">-->
|
||
<!-- <td>-->
|
||
<!-- 20230385162-->
|
||
<!-- </td>-->
|
||
<!-- <td>续费</td>-->
|
||
<!-- <td>-->
|
||
<!-- ¥880-->
|
||
<!-- </td>-->
|
||
<!-- <td>已支付</td>-->
|
||
<!-- <td>{{ user.createTime }}</td>-->
|
||
<!-- </tr>-->
|
||
<!-- </tbody>-->
|
||
<!-- </table>-->
|
||
<!-- </a-card>-->
|
||
<a-divider style="height: 8px" />
|
||
<a-card title="合同文件" :bordered="false" v-any-role="['superAdmin','admin']">
|
||
<a-row :gutter="16">
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="合同文件" name="parentId">
|
||
<a-upload
|
||
v-model:file-list="file1"
|
||
name="file"
|
||
action="https://file.wsdns.cn/api/file/image"
|
||
:headers="{ Authorization: token }"
|
||
@change="onFile1"
|
||
>
|
||
<a-button>
|
||
<upload-outlined />
|
||
上传
|
||
</a-button>
|
||
</a-upload>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="报价单" name="parentId">
|
||
<a-upload
|
||
v-model:file-list="file2"
|
||
name="file"
|
||
action="https://file.wsdns.cn/api/file/image"
|
||
:headers="{ Authorization: token }"
|
||
@change="onFile2"
|
||
>
|
||
<a-button>
|
||
<upload-outlined />
|
||
上传
|
||
</a-button>
|
||
</a-upload>
|
||
</a-form-item>
|
||
</a-col>
|
||
<a-col
|
||
v-bind="styleResponsive ? { md: 8, sm: 24, xs: 24 } : { span: 8 }"
|
||
>
|
||
<a-form-item label="其他" name="parentId">
|
||
<a-upload
|
||
v-model:file-list="file3"
|
||
name="file"
|
||
action="https://file.wsdns.cn/api/file/image"
|
||
:headers="{ Authorization: token }"
|
||
@change="onFile3"
|
||
>
|
||
<a-button>
|
||
<upload-outlined />
|
||
上传
|
||
</a-button>
|
||
</a-upload>
|
||
</a-form-item>
|
||
</a-col>
|
||
</a-row>
|
||
</a-card>
|
||
</a-form>
|
||
</div>
|
||
</ele-modal>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, reactive, watch, createVNode, computed } from 'vue';
|
||
import { Form, message } from 'ant-design-vue';
|
||
import { assignObject, ipReg, toDateString } from "ele-admin-pro";
|
||
import { addApp, updateApp, getAppSecret, updateAppSecret } from '@/api/oa/app';
|
||
import type { App } from '@/api/oa/app/model';
|
||
import { useUserStore } from '@/store/modules/user';
|
||
import { useThemeStore } from '@/store/modules/theme';
|
||
import { storeToRefs } from 'pinia';
|
||
// 中文语言文件
|
||
import zh_Hans from 'bytemd/locales/zh_Hans.json';
|
||
// 链接、删除线、复选框、表格等的插件
|
||
// 插件的中文语言文件
|
||
import zh_HansGfm from '@bytemd/plugin-gfm/locales/zh_Hans.json';
|
||
import 'bytemd/dist/index.min.css';
|
||
import 'github-markdown-css/github-markdown-light.css';
|
||
import { Modal } from 'ant-design-vue';
|
||
// import TinymceEditor from '@/components/TinymceEditor/index.vue';
|
||
import highlight from '@bytemd/plugin-highlight';
|
||
// 中文语言文件
|
||
import gfm from '@bytemd/plugin-gfm';
|
||
// // 预览界面的样式,这里用的 github 的 markdown 主题
|
||
import 'github-markdown-css/github-markdown-light.css';
|
||
import {
|
||
EyeOutlined,
|
||
EyeInvisibleOutlined,
|
||
ExclamationCircleOutlined
|
||
} from '@ant-design/icons-vue';
|
||
import { UploadOutlined } from '@/layout/menu-icons';
|
||
import { FormInstance } from 'ant-design-vue/es/form';
|
||
import { Company } from '@/api/system/company/model';
|
||
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||
import DictSelect from '@/views/search/components/dict-select.vue';
|
||
import { User } from '@/api/system/user/model';
|
||
import { uploadFile } from "@/api/system/file";
|
||
import { FILE_SERVER, FILE_THUMBNAIL, TOKEN_STORE_NAME } from "@/config/setting";
|
||
import { listAppUser, addAppUser, removeAppUser } from "@/api/oa/app/user";
|
||
import { AppUser } from '@/api/oa/app/user/model'
|
||
import { pageCompany } from "@/api/system/company";
|
||
import { Task } from "@/api/oa/task/model";
|
||
|
||
// 是否是修改
|
||
const isUpdate = ref(false);
|
||
const useForm = Form.useForm;
|
||
// 是否开启响应式布局
|
||
const themeStore = useThemeStore();
|
||
const { styleResponsive } = storeToRefs(themeStore);
|
||
|
||
const props = defineProps<{
|
||
// 弹窗是否打开
|
||
visible: boolean;
|
||
// 修改回显的数据
|
||
data?: App | null;
|
||
}>();
|
||
|
||
const emit = defineEmits<{
|
||
(e: 'done'): void;
|
||
(e: 'update:visible', visible: boolean): void;
|
||
}>();
|
||
|
||
// 提交状态
|
||
const loading = ref(false);
|
||
// 是否显示最大化切换按钮
|
||
const maxable = ref(true);
|
||
// 当期时间
|
||
const dayTime = toDateString(new Date());
|
||
// 已上传数据
|
||
const logo = ref<ItemType[]>([]);
|
||
const images = ref<ItemType[]>([]);
|
||
const appUsers = ref<AppUser[]>([]);
|
||
const tasks = ref<Task[]>([]);
|
||
const selectUser = ref<string>();
|
||
const editStatus = ref(false);
|
||
// 类型 single|multiple
|
||
// const type = ref<string>('single');
|
||
// 日期范围选择
|
||
const dateRange = ref<[string, string]>(['', '']);
|
||
const content = ref('');
|
||
const requirement = ref('');
|
||
// 插件
|
||
const plugins = ref([
|
||
gfm({
|
||
locale: zh_HansGfm
|
||
}),
|
||
highlight()
|
||
]);
|
||
|
||
// 穿梭框数据
|
||
const breakfast = ref<any[]>();
|
||
const lunch = ref<any[]>();
|
||
const targetKeys2 = ref<any[]>([]);
|
||
const showAppSecret = ref(false);
|
||
const dinner = ref<any[]>();
|
||
const targetKeys3 = ref<any[]>([]);
|
||
const selectedKeys3 = ref<any[]>();
|
||
const selectedGoodsIds = ref<any[]>([]);
|
||
const showAddUserForm = ref(false);
|
||
const token = localStorage.getItem(TOKEN_STORE_NAME);
|
||
const formRef = ref<FormInstance | null>(null);
|
||
|
||
// 文件模型
|
||
interface FileItem {
|
||
uid: string;
|
||
name?: string;
|
||
status?: string;
|
||
response?: Response;
|
||
thumbUrl?: string;
|
||
downloadUrl?: string;
|
||
url: string;
|
||
}
|
||
|
||
interface FileInfo {
|
||
file: FileItem;
|
||
fileList: FileItem[];
|
||
}
|
||
// 文件上传
|
||
const file1 = ref<FileItem[]>();
|
||
const file2 = ref<FileItem[]>();
|
||
const file3 = ref<FileItem[]>();
|
||
|
||
// 用户信息
|
||
const form = reactive<App>({
|
||
// 应用id
|
||
appId: undefined,
|
||
// 应用秘钥
|
||
appSecret: '',
|
||
enName: '',
|
||
// 应用名称
|
||
appName: '',
|
||
// 上级id, 0是顶级
|
||
parentId: undefined,
|
||
// 应用编号
|
||
appCode: '',
|
||
// 应用图标
|
||
appIcon: '',
|
||
appQrcode: '',
|
||
// 应用截图
|
||
images: '',
|
||
// 应用类型
|
||
appType: '',
|
||
appTypeMultiple: undefined,
|
||
// 菜单类型
|
||
menuType: undefined,
|
||
// 应用地址
|
||
appUrl: '',
|
||
// 后台管理地址
|
||
adminUrl: undefined,
|
||
// 下载地址
|
||
downUrl: undefined,
|
||
serverUrl: undefined,
|
||
callbackUrl: undefined,
|
||
gitUrl: undefined,
|
||
docsUrl: undefined,
|
||
prototypeUrl: undefined,
|
||
ipAddress: undefined,
|
||
fileUrl: undefined,
|
||
// 应用包名
|
||
packageName: '',
|
||
// 点击次数
|
||
clicks: '',
|
||
// 安装次数
|
||
installs: '',
|
||
// 应用介绍
|
||
content: '',
|
||
// 开发者(个人)
|
||
developer: '',
|
||
director: '',
|
||
projectDirector: '',
|
||
salesman: '',
|
||
// 软件定价
|
||
price: '',
|
||
// 评分
|
||
score: '',
|
||
// 星级
|
||
star: '',
|
||
// 菜单组件地址
|
||
component: '',
|
||
// 菜单路由地址
|
||
path: '',
|
||
// 权限标识
|
||
authority: '',
|
||
// 打开位置
|
||
target: '',
|
||
// 是否隐藏, 0否, 1是(仅注册路由不显示在左侧菜单)
|
||
hide: undefined,
|
||
// 菜单侧栏选中的path
|
||
active: '',
|
||
// 其它路由元信息
|
||
meta: '',
|
||
// 版本
|
||
edition: '',
|
||
// 版本号
|
||
version: '',
|
||
// 是否已安装
|
||
isUse: undefined,
|
||
// 排序
|
||
sortNumber: undefined,
|
||
// 备注
|
||
comments: '',
|
||
tenantName: '',
|
||
companyName: '',
|
||
// 租户编号
|
||
tenantCode: '',
|
||
// 租户id
|
||
tenantId: undefined,
|
||
// 创建时间
|
||
createTime: '',
|
||
appStatus: '开发中',
|
||
// 状态
|
||
status: undefined,
|
||
// 发布者
|
||
userId: '',
|
||
// 发布者昵称
|
||
nickname: '',
|
||
// 子菜单
|
||
children: [],
|
||
// 权限树回显选中状态, 0未选中, 1选中
|
||
checked: false,
|
||
//
|
||
key: undefined,
|
||
//
|
||
value: undefined,
|
||
//
|
||
parentIds: [],
|
||
//
|
||
openType: undefined,
|
||
//
|
||
search: undefined,
|
||
// 成员管理
|
||
users: [],
|
||
// 项目需求
|
||
requirement: '',
|
||
file1: '[]',
|
||
file2: '[]',
|
||
file3: '[]',
|
||
|
||
});
|
||
|
||
/* 更新visible */
|
||
const updateVisible = (value: boolean) => {
|
||
emit('update:visible', value);
|
||
};
|
||
|
||
// 表单验证规则
|
||
const rules = reactive({
|
||
appName: [
|
||
{
|
||
required: true,
|
||
type: 'string',
|
||
message: '请输入应用名称',
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
appCode: [
|
||
{
|
||
required: true,
|
||
type: 'string',
|
||
message: '请输入应用标识(英文字母)',
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
ipAddress: [
|
||
{
|
||
pattern: ipReg,
|
||
message: 'IP地址不合法',
|
||
type: 'string'
|
||
}
|
||
],
|
||
appStatus: [
|
||
{
|
||
required: true,
|
||
type: 'string',
|
||
message: '请选择应用状态',
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
// progress: [
|
||
// {
|
||
// required: true,
|
||
// type: 'string',
|
||
// message: '请选择跟进状态',
|
||
// trigger: 'blur'
|
||
// }
|
||
// ],
|
||
// securityStatus: [
|
||
// {
|
||
// required: true,
|
||
// type: 'string',
|
||
// message: '请选择安全状态',
|
||
// trigger: 'blur'
|
||
// }
|
||
// ],
|
||
// companyName: [
|
||
// {
|
||
// required: true,
|
||
// type: 'string',
|
||
// message: '请选择租赁单位',
|
||
// trigger: 'blur'
|
||
// }
|
||
// ],
|
||
// appRegion: [
|
||
// {
|
||
// required: true,
|
||
// type: 'string',
|
||
// message: '请输入应用地址',
|
||
// trigger: 'blur'
|
||
// }
|
||
// ]
|
||
});
|
||
|
||
const { resetFields, validate, validateInfos } = useForm(form, rules);
|
||
|
||
const addUser = () => {
|
||
showAddUserForm.value = true;
|
||
};
|
||
|
||
const chooseCompanyName = (data: Company) => {
|
||
form.companyName = data.companyName;
|
||
form.companyId = data.companyId;
|
||
form.tenantId = data.tenantId;
|
||
};
|
||
|
||
// 添加开发成员
|
||
const addAppDevUser = (data: User) => {
|
||
addAppUser({
|
||
userId: data.userId,
|
||
appId: props.data?.appId,
|
||
role: 20
|
||
}).then((msg) => {
|
||
message.success(msg);
|
||
reload();
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
}
|
||
|
||
// 添加体验成员
|
||
const addAppExpUser = (data: User) => {
|
||
addAppUser({
|
||
userId: data.userId,
|
||
appId: props.data?.appId,
|
||
role: 10
|
||
}).then((msg) => {
|
||
message.success(msg);
|
||
reload();
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
}
|
||
|
||
// 移除成员
|
||
const removeUser = (data: AppUser) => {
|
||
removeAppUser(data.appUserId)
|
||
.then((msg) => {
|
||
message.success(msg);
|
||
reload();
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
};
|
||
|
||
const handleEditStatus = () => {
|
||
editStatus.value = !editStatus.value;
|
||
}
|
||
|
||
const chooseDirector = (data: User) => {
|
||
form.director = data.nickname;
|
||
};
|
||
|
||
const chooseDeveloper = (data: Company) => {
|
||
form.developer = data.companyName;
|
||
};
|
||
|
||
const chooseSalesman = (data: User) => {
|
||
form.salesman = data.nickname;
|
||
};
|
||
|
||
const changeAppCode = (e) => {
|
||
form.packageName = `com.gxwebsoft.${form.appCode}`;
|
||
form.appUrl = `https://apps.gxwebsoft.com/${form.appCode}`;
|
||
}
|
||
|
||
const onFile1 = (info: FileInfo) => {
|
||
let resFileList = [...info.fileList];
|
||
file1.value = resFileList.map((file) => {
|
||
if (file.response) {
|
||
file.url = file.response.url;
|
||
}
|
||
return file;
|
||
});
|
||
form.file1 = JSON.stringify(file1.value);
|
||
};
|
||
const onFile2 = (info: FileInfo) => {
|
||
let resFileList = [...info.fileList];
|
||
file2.value = resFileList.map((file) => {
|
||
if (file.response) {
|
||
file.url = file.response.url;
|
||
}
|
||
return file;
|
||
});
|
||
form.file2 = JSON.stringify(file2.value);
|
||
};
|
||
const onFile3 = (info: FileInfo) => {
|
||
let resFileList = [...info.fileList];
|
||
file3.value = resFileList.map((file) => {
|
||
if (file.response) {
|
||
file.url = file.response.url;
|
||
}
|
||
return file;
|
||
});
|
||
form.file3 = JSON.stringify(file3.value);
|
||
};
|
||
|
||
// 查看秘钥
|
||
const onAppSecret = (appId) => {
|
||
getAppSecret({ appId }).then((res) => {
|
||
console.log(res);
|
||
form.appSecret = res.data?.appSecret
|
||
showAppSecret.value = !showAppSecret.value;
|
||
});
|
||
};
|
||
// 重置秘钥
|
||
const onUpdateAppSecret = (appId) => {
|
||
Modal.confirm({
|
||
title: '提示',
|
||
content:
|
||
'重置AppSecret将会影响应用的正常运行,重置成功后请及时更换新的秘钥!确定要重置吗?',
|
||
icon: createVNode(ExclamationCircleOutlined),
|
||
maskClosable: true,
|
||
onOk: () => {
|
||
updateAppSecret({ appId }).then((res) => {
|
||
form.appSecret = res.data?.appSecret
|
||
message.success(res.message);
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
const config = ref({
|
||
height: 500,
|
||
images_upload_handler: (blobInfo, success, error) => {
|
||
const file = blobInfo.blob();
|
||
// 使用 axios 上传,实际开发这段建议写在 api 中再调用 api
|
||
const formData = new FormData();
|
||
formData.append('file', file, file.name);
|
||
uploadFile(<File>file)
|
||
.then((result) => {
|
||
if (result.length) {
|
||
if (file.size / 1024 / 1024 > 2) {
|
||
error('图片大小不能超过 2MB');
|
||
}
|
||
success(FILE_SERVER + result.path);
|
||
} else {
|
||
error('上传失败');
|
||
}
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
},
|
||
// 自定义文件上传(这里使用把选择的文件转成 blob 演示)
|
||
// file_picker_callback: (callback: any, _value: any, meta: any) => {
|
||
// const input = document.createElement("input");
|
||
// input.setAttribute("type", "file");
|
||
// // 设定文件可选类型
|
||
// if (meta.filetype === "image") {
|
||
// input.setAttribute("accept", "image/*");
|
||
// } else if (meta.filetype === "media") {
|
||
// input.setAttribute("accept", "video/*");
|
||
// }
|
||
// input.onchange = () => {
|
||
// const file = input.files?.[0];
|
||
// console.log(file);
|
||
// if (!file) {
|
||
// return;
|
||
// }
|
||
// if (meta.filetype === "media") {
|
||
// if (!file.type.startsWith("video/")) {
|
||
// editorRef.value?.alert({ content: "只能选择视频文件" });
|
||
// return;
|
||
// }
|
||
// }
|
||
// if (file.size / 1024 / 1024 > 20) {
|
||
// editorRef.value?.alert({ content: "大小不能超过 20MB" });
|
||
// return;
|
||
// }
|
||
// const reader = new FileReader();
|
||
// reader.onload = (e) => {
|
||
// if (e.target?.result != null) {
|
||
// const blob = new Blob([e.target.result], { type: file.type });
|
||
// callback(URL.createObjectURL(blob));
|
||
// }
|
||
// };
|
||
// reader.readAsArrayBuffer(file);
|
||
// };
|
||
// input.click();
|
||
// }
|
||
});
|
||
|
||
/* 粘贴图片上传服务器并插入编辑器 */
|
||
const onPaste = (e) => {
|
||
console.log(e);
|
||
const items = (e.clipboardData || e.originalEvent.clipboardData).items;
|
||
console.log(items);
|
||
let hasFile = false;
|
||
for (let i = 0; i < items.length; i++) {
|
||
if (items[i].type.indexOf('image') !== -1) {
|
||
let file = items[i].getAsFile();
|
||
const item: ItemType = {
|
||
file,
|
||
uid: (file as any).lastModified,
|
||
name: file.name
|
||
};
|
||
uploadFile(<File>item.file)
|
||
.then((result) => {
|
||
const addPath = '\n\r';
|
||
content.value = content.value + addPath
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
hasFile = true;
|
||
}
|
||
}
|
||
if (hasFile) {
|
||
e.preventDefault();
|
||
}
|
||
}
|
||
|
||
/* 保存编辑 */
|
||
const save = () => {
|
||
if (!formRef.value) {
|
||
return;
|
||
}
|
||
formRef.value
|
||
.validate()
|
||
.then(() => {
|
||
loading.value = true;
|
||
const formData = {
|
||
...form,
|
||
content: content.value,
|
||
requirement: requirement.value,
|
||
search: form.search ? 1 : 0,
|
||
images: JSON.stringify(images.value),
|
||
appTypeMultiple: JSON.stringify(form.appTypeMultiple)
|
||
};
|
||
const saveOrUpdate = isUpdate.value ? updateApp : addApp;
|
||
saveOrUpdate(formData)
|
||
.then((msg) => {
|
||
loading.value = false;
|
||
message.success(msg);
|
||
updateVisible(false);
|
||
emit('done');
|
||
})
|
||
.catch((e) => {
|
||
loading.value = false;
|
||
message.error(e.message);
|
||
});
|
||
})
|
||
.catch(() => {});
|
||
};
|
||
|
||
// 上传文件:应用图标
|
||
const onUploadIcon = (item) => {
|
||
const { file } = item;
|
||
logo.value = [];
|
||
uploadFile(file)
|
||
.then((data) => {
|
||
logo.value.push({
|
||
uid: data.id,
|
||
url: FILE_THUMBNAIL + data.path,
|
||
status: 'done'
|
||
});
|
||
form.appIcon = data.path;
|
||
message.success('上传成功');
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
};
|
||
|
||
/* 上传事件 */
|
||
const uploadHandler = (file: File) => {
|
||
const item: ItemType = {
|
||
file,
|
||
uid: (file as any).uid,
|
||
name: file.name
|
||
};
|
||
if (!file.type.startsWith('image')) {
|
||
message.error('只能选择图片');
|
||
return;
|
||
}
|
||
if (file.size / 1024 / 1024 > 2) {
|
||
message.error('大小不能超过 2MB');
|
||
return;
|
||
}
|
||
onUpload(item);
|
||
};
|
||
|
||
// 上传文件
|
||
const onUpload = (item) => {
|
||
const { file } = item;
|
||
uploadFile(file)
|
||
.then((data) => {
|
||
images.value.push({
|
||
uid: data.id,
|
||
url: FILE_SERVER + data.path,
|
||
status: 'done'
|
||
});
|
||
})
|
||
.catch((e) => {
|
||
message.error(e.message);
|
||
});
|
||
};
|
||
|
||
const reload = () => {
|
||
loading.value = true;
|
||
// 加载项目成员
|
||
listAppUser({appId:props.data?.appId}).then(data => {
|
||
appUsers.value = data
|
||
})
|
||
// 加载企业信息
|
||
const companyId = props.data?.companyId;
|
||
pageCompany({companyId}).then(res => {
|
||
form.tenantId = undefined;
|
||
const list = res?.list;
|
||
if(list && list.length > 0){
|
||
form.tenantId = list[0].tenantId;
|
||
}
|
||
})
|
||
};
|
||
|
||
watch(
|
||
() => props.visible,
|
||
(visible) => {
|
||
if (visible) {
|
||
content.value = '';
|
||
requirement.value = '';
|
||
if (props.data) {
|
||
assignObject(form, props.data);
|
||
logo.value = [];
|
||
images.value = [];
|
||
if(props.data.appIcon){
|
||
logo.value.push({
|
||
uid: 0,
|
||
url: FILE_THUMBNAIL + props.data.appIcon,
|
||
status: 'done'
|
||
});
|
||
}
|
||
if(props.data.images){
|
||
const arr = JSON.parse(props.data.images);
|
||
arr.map((d, i) => {
|
||
images.value.push({
|
||
uid: d.uid,
|
||
url: d.url,
|
||
status: 'done'
|
||
});
|
||
});
|
||
}
|
||
if(props.data.appTypeMultiple){
|
||
form.appTypeMultiple = JSON.parse(props.data.appTypeMultiple)
|
||
}
|
||
if(props.data.search){
|
||
form.search = props.data.search == 1 ? true : 0;
|
||
}
|
||
if (props.data.content) {
|
||
content.value = props.data.content;
|
||
}
|
||
if (props.data.requirement){
|
||
requirement.value = props.data.requirement;
|
||
}
|
||
if (props.data.file1) {
|
||
file1.value = JSON.parse(props.data.file1);
|
||
}
|
||
if (props.data.file2) {
|
||
file2.value = JSON.parse(props.data.file2);
|
||
}
|
||
if (props.data.file3) {
|
||
file3.value = JSON.parse(props.data.file3);
|
||
}
|
||
isUpdate.value = true;
|
||
reload();
|
||
} else {
|
||
isUpdate.value = false;
|
||
}
|
||
} else {
|
||
resetFields();
|
||
}
|
||
}
|
||
);
|
||
</script>
|
||
<style lang="less"></style>
|
||
|
||
<style lang="less">
|
||
.tab-pane {
|
||
min-height: 300px;
|
||
}
|
||
|
||
.ml-10 {
|
||
margin-left: 5px;
|
||
}
|
||
|
||
.upload-text {
|
||
margin-right: 70px;
|
||
}
|
||
|
||
.upload-image {
|
||
margin-bottom: 30px;
|
||
display: flex;
|
||
justify-content: center;
|
||
}
|
||
</style>
|