Initial commit

This commit is contained in:
南宁网宿科技
2023-06-03 23:57:39 +08:00
commit 13bed2bafb
1017 changed files with 175796 additions and 0 deletions
@@ -0,0 +1,466 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
width="75%"
:visible="visible"
:confirm-loading="loading"
:maxable="maxAble"
:title="isUpdate ? '编辑服务器' : '添加服务器'"
:body-style="{ paddingBottom: '8px' }"
@update:visible="updateVisible"
:maskClosable="false"
@ok="save"
>
<a-tabs v-model:activeKey="activeKey" type="card">
<a-tab-pane key="1" tab="基本信息">
<a-form
:label-col="{ md: { span: 8 }, sm: { span: 24 } }"
:wrapper-col="{ md: { span: 24 }, sm: { span: 24 } }"
layout="vertical"
>
<a-form-item label="服务器名称" v-bind="validateInfos.name">
<a-input
allow-clear
:maxlength="20"
placeholder="请输入服务器名称"
v-model:value="form.name"
@blur="validate('name', { trigger: 'blur' }).catch(() => {})"
/>
</a-form-item>
<a-form-item label="服务器IP" v-bind="validateInfos.code">
<a-input
allow-clear
:maxlength="20"
placeholder="请输入合法的公网IP,如:0.0.0.0"
v-model:value="form.code"
/>
</a-form-item>
<a-form-item label="品牌厂商" v-bind="validateInfos.brand">
<brand-select
v-model:value="form.brand"
@blur="validate('brand', { trigger: 'blur' }).catch(() => {})"
/>
</a-form-item>
<a-form-item label="所属客户" v-bind="validateInfos.companyId">
<SelectCompany
:placeholder="`请选择客户`"
v-model:value="form.customerName"
@done="onSelect"
/>
</a-form-item>
<a-form-item label="备注" v-bind="validateInfos.comments">
<!-- 编辑器 -->
<byte-md-editor
v-model:value="form.comments"
:locale="zh_Hans"
:plugins="plugins"
height="300px"
:editorConfig="{ lineNumbers: true }"
/>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="2" tab="账号密码">
<a-form
:label-col="{ md: { span: 8 }, sm: { span: 24 } }"
:wrapper-col="{ md: { span: 24 }, sm: { span: 24 } }"
layout="vertical"
>
<a-form-item label="宝塔面板" v-bind="validateInfos.panel">
<a-input
allow-clear
placeholder="http://ip:8888"
v-model:value="form.panel"
@blur="validate('panel', { trigger: 'blur' }).catch(() => {})"
/>
</a-form-item>
<a-form-item label="宝塔面板账号" v-bind="validateInfos.panelAccount">
<a-input
allow-clear
:maxlength="20"
placeholder="宝塔面板账号"
v-model:value="form.panelAccount"
@blur="
validate('panelAccount', { trigger: 'blur' }).catch(() => {})
"
/>
</a-form-item>
<a-form-item
label="宝塔面板密码"
v-bind="validateInfos.panelPassword"
>
<a-input
allow-clear
:maxlength="20"
placeholder="宝塔面板密码"
v-model:value="form.panelPassword"
@blur="
validate('panelPassword', { trigger: 'blur' }).catch(() => {})
"
/>
</a-form-item>
<a-form-item label="宝塔API接口秘钥" v-bind="validateInfos.btSign">
<a-input
allow-clear
:maxlength="50"
placeholder="宝塔API接口秘钥"
v-model:value="form.btSign"
@blur="validate('btSign', { trigger: 'blur' }).catch(() => {})"
/>
</a-form-item>
<a-form-item label="服务器账号" v-bind="validateInfos.account">
<a-input v-model:value="form.account" placeholder="root" />
</a-form-item>
<a-form-item label="服务器密码" v-bind="validateInfos.password">
<a-input
allow-clear
:maxlength="20"
placeholder="服务器密码"
v-model:value="form.password"
@blur="validate('password', { trigger: 'blur' }).catch(() => {})"
/>
</a-form-item>
<a-form-item label="厂商登录账号" v-bind="validateInfos.brandAccount">
<a-input
allow-clear
:maxlength="20"
placeholder="请输入阿里云/腾讯登录账号"
v-model:value="form.brandAccount"
/>
</a-form-item>
<a-form-item label="登录密码" v-bind="validateInfos.brandPassword">
<a-input
allow-clear
:maxlength="20"
placeholder="登录密码"
v-model:value="form.brandPassword"
/>
</a-form-item>
</a-form>
</a-tab-pane>
<a-tab-pane key="3" tab="其他配置">
<a-form
:label-col="{ md: { span: 8 }, sm: { span: 24 } }"
:wrapper-col="{ md: { span: 24 }, sm: { span: 24 } }"
layout="vertical"
>
<a-form-item label="服务器配置" v-bind="validateInfos.configuration">
<configuration-select
v-model:value="form.configuration"
@blur="
validate('configuration', { trigger: 'blur' }).catch(() => {})
"
/>
</a-form-item>
<a-form-item label="购买日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择购买日期"
v-model:value="startTime"
/>
</a-form-item>
<a-form-item label="结束日期">
<a-date-picker
class="ele-fluid"
placeholder="请选择结束日期"
v-model:value="endTime"
/>
</a-form-item>
<a-form-item label="开放端口" v-bind="validateInfos.openPort">
<port-select
v-model:value="form.openPort"
@blur="validate('openPort', { trigger: 'blur' }).catch(() => {})"
/>
</a-form-item>
<a-form-item label="状态" v-bind="validateInfos.status">
<a-select
optionFilterProp="label"
placeholder="请选择状态"
:options="serverStatus"
allow-clear
v-model:value="form.status"
/>
</a-form-item>
<a-form-item label="排序" v-bind="validateInfos.sortNumber">
<a-input
allow-clear
:maxlength="20"
placeholder="排序"
v-model:value="form.sortNumber"
/>
</a-form-item>
</a-form>
</a-tab-pane>
</a-tabs>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { Form, message } from 'ant-design-vue';
import { assignObject, ipReg } from 'ele-admin-pro';
import type { Assets } from '@/api/oa/assets/model';
import BrandSelect from './brand-select.vue';
import SelectUser from '@/components/SelectUser/index.vue';
import ConfigurationSelect from './configuration-select.vue';
import PortSelect from './port-select.vue';
import { addAssets, updateAssets } from '@/api/oa/assets';
import dayjs from 'dayjs';
import type { Dayjs } from 'dayjs';
import { getDictionaryOptions } from '@/utils/common';
import { UserVisibility } from '@/api/user/visibility/model';
// 中文语言文件
import zh_Hans from 'bytemd/locales/zh_Hans.json';
import 'bytemd/dist/index.min.css';
import 'github-markdown-css/github-markdown-light.css';
// // 链接、删除线、复选框、表格等的插件
import gfm from '@bytemd/plugin-gfm';
// // 插件的中文语言文件
import zh_HansGfm from '@bytemd/plugin-gfm/locales/zh_Hans.json';
// 中文语言文件
import 'bytemd/dist/index.min.css';
import highlight from '@bytemd/plugin-highlight-ssr';
import 'highlight.js/styles/default.css';
import { Company } from "@/api/system/company/model";
const useForm = Form.useForm;
// 是否是修改
const isUpdate = ref(false);
// 是否显示最大化切换按钮
const maxAble = ref(true);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Assets | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
const serverStatus = getDictionaryOptions('serverStatus');
const users = ref<UserVisibility[]>([]);
// 用户信息
const form = reactive<Assets>({
code: '',
createTime: '',
endTime: '',
name: '',
brand: undefined,
account: '',
password: '',
configuration: '不详',
panelAccount: '',
panelPassword: '',
panel: '',
financeAmount: 0,
financeYears: 0,
financeRenew: 0,
customerId: undefined,
customerName: '',
financeCustomerName: '',
financeCustomerContact: '',
financeCustomerPhone: '',
brandAccount: '',
brandPassword: '',
btSign: '',
openPort: '80',
comments: '',
root: 'root',
sortNumber: 100,
status: '0',
type: '',
assetsId: 0,
visibility: 'private',
userList: [],
userId: undefined
});
// 选项卡位置
const activeKey = ref('1');
// 购买日期
const startTime = ref<Dayjs>();
// 结束日期
const endTime = ref<Dayjs>();
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 表单验证规则
const rules = reactive({
name: [
{
required: true,
type: 'string',
message: '请输入服务器名称',
trigger: 'blur'
}
],
userId: [
{
required: true,
type: 'number',
message: '请选择客户'
}
],
code: [
{
required: true,
type: 'string',
message: '请输入IP地址',
trigger: 'blur'
},
{
pattern: ipReg,
message: 'IP地址不合法',
type: 'string'
}
],
brand: [
{
required: true,
type: 'string',
message: '请选项服务器厂商',
trigger: 'blur'
}
],
configuration: [
{
required: true,
type: 'string',
message: '请选择服务器配置',
trigger: 'blur'
}
],
openPort: [
{
required: true,
type: 'any',
message: '请选择服务器已开放端',
trigger: 'blur'
}
],
panel: [
{
required: true,
type: 'string',
message: '请填写面板地址',
trigger: 'blur'
}
],
panelAccount: [
{
required: true,
type: 'string',
message: '请填写面板登录账号',
trigger: 'blur'
}
],
panelPassword: [
{
required: true,
type: 'string',
message: '请填写面板登录密码',
trigger: 'blur'
}
],
status: [
{
required: true,
type: 'string',
message: '服务器状态',
trigger: 'blur'
}
],
visibility: [
{
required: true,
type: 'string',
message: '请设置可见性'
}
]
});
const { resetFields, validate, validateInfos } = useForm(form, rules);
const onSelect = (data: Company) => {
form.customerId = data.companyId;
form.customerName = data.companyName;
};
// 插件
const plugins = ref([
gfm({
locale: zh_HansGfm
}),
highlight()
]);
/* 保存编辑 */
const save = () => {
validate()
.then(() => {
loading.value = true;
const data = {
...form,
startTime: startTime.value?.format('YYYY-MM-DD HH:mm:ss'),
endTime: endTime.value?.format('YYYY-MM-DD HH:mm:ss')
};
// 转字符串
data.openPort = data.openPort.toString();
const saveOrUpdate = isUpdate.value ? updateAssets : addAssets;
console.log(data);
saveOrUpdate(data)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = false;
assignObject(form, props.data);
if (props.data.startTime) {
startTime.value = dayjs(props.data.startTime, 'YYYY-MM-DD');
endTime.value = dayjs(props.data.endTime, 'YYYY-MM-DD');
} else {
startTime.value = undefined;
endTime.value = undefined;
}
if (props.data.openPort) {
form.openPort = props.data.openPort.split(',');
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
startTime.value = undefined;
endTime.value = undefined;
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 300px;
}
</style>
@@ -0,0 +1,251 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
width="75%"
:visible="visible"
:confirm-loading="loading"
:maxable="maxAble"
:title="'服务器详情'"
:body-style="{ paddingBottom: '36px' }"
@update:visible="updateVisible"
:footer="null"
>
<a-form
:label-col="{ md: { span: 4 }, sm: { span: 24 } }"
:wrapper-col="{ md: { span: 19 }, sm: { span: 24 } }"
>
<div class="base-form" style="margin-bottom: 20px">
<a-descriptions :title="assets.name" bordered>
<a-descriptions-item label="客户名称">
{{ assets.companyName }}
</a-descriptions-item>
<a-descriptions-item label="服务器名称">
{{ assets.name }}
</a-descriptions-item>
<a-descriptions-item label="状态">
<a-tag v-if="assets.status === '0'" color="green">正常</a-tag>
<a-tag v-if="assets.status === '1'" color="red">过期</a-tag>
<a-tag v-if="assets.status === '2'" color="purple">不再续</a-tag>
</a-descriptions-item>
<a-descriptions-item label="服务器IP">
{{ assets.code }}
</a-descriptions-item>
<a-descriptions-item label="宝塔面板">
<a href="#" @click="openUrl(assets)">{{ assets.panel }}</a>
</a-descriptions-item>
<a-descriptions-item label="宝塔账号">
{{ assets.panelAccount }}
</a-descriptions-item>
<a-descriptions-item label="宝塔密码">
<span v-role="['admin']">{{ assets.panelPassword }}</span>
<span v-any-role="['user', 'guest', 'superAdmin', 'developer']"
>****</span
>
</a-descriptions-item>
<a-descriptions-item label="品牌厂商">
{{ assets.brand }}
</a-descriptions-item>
<a-descriptions-item label="服务器配置">
{{ assets.configuration }}
</a-descriptions-item>
<a-descriptions-item label="开发端口">
{{ assets.openPort }}
</a-descriptions-item>
<a-descriptions-item label="有效期">
<div v-if="assets.startTime && assets.endTime">
<div class="ele-text-info">{{ assets.startTime }}</div>
<div v-html="getEndTime(assets.endTime)"></div>
</div>
</a-descriptions-item>
<a-descriptions-item label="服务器账号">
<span class="ele-text-danger">root</span>
</a-descriptions-item>
<a-descriptions-item label="服务器密码">
<span class="ele-text-danger" v-role="['admin']">{{
assets.password
}}</span>
<span v-any-role="['user', 'guest', 'superAdmin', 'developer']"
>****</span
>
</a-descriptions-item>
<a-descriptions-item label="创建时间">
{{ timeAgo(assets.createTime) }}
</a-descriptions-item>
<a-descriptions-item label="排序">
{{ assets.sortNumber }}
</a-descriptions-item>
</a-descriptions>
</div>
<div class="base-form" style="margin-bottom: 20px">
<a-descriptions title="服务器状态" bordered v-if="assets.systemTotal">
<a-descriptions-item label="操作系统信息">
{{ assets.systemTotal.system }}
</a-descriptions-item>
<a-descriptions-item label="面板版本">
{{ assets.systemTotal.version }}
</a-descriptions-item>
<a-descriptions-item label="已不间断运行">
{{ assets.systemTotal.time }}
</a-descriptions-item>
<a-descriptions-item label="CPU 核心数">
{{ assets.systemTotal.cpuNum }}
</a-descriptions-item>
<a-descriptions-item label="负载状态">
{{ assets.systemTotal.cpuRealUsed }}
</a-descriptions-item>
<a-descriptions-item label="物理内存容量(MB)">
{{ assets.systemTotal.memTotal }}
</a-descriptions-item>
<a-descriptions-item label="已使用的物理内存(MB)">
{{ assets.systemTotal.memRealUsed }}
</a-descriptions-item>
<a-descriptions-item label="可用的物理内存(MB)">
{{ assets.systemTotal.memFree }}
</a-descriptions-item>
<a-descriptions-item label="缓存化的内存(MB)">
{{ assets.systemTotal.memCached }}
</a-descriptions-item>
<a-descriptions-item label="系统缓冲 (MB)">
{{ assets.systemTotal.memBuffers }}
</a-descriptions-item>
</a-descriptions>
</div>
<!-- <a-space v-if="assets.systemTotal">-->
<!-- <div class="base-form" style="margin-bottom: 20px">-->
<!-- <a-tabs v-model:activeKey="activeKey">-->
<!-- <a-tab-pane key="1" tab="系统信息" force-render>-->
<!-- &lt;!&ndash; <byte-md-viewer :value="assets.comments" :plugins="plugins" />&ndash;&gt;-->
<!-- </a-tab-pane>-->
<!-- </a-tabs>-->
<!-- </div>-->
<!-- </a-space>-->
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { getEndTime } from '@/utils/common';
import { ref, reactive, watch } from 'vue';
import { Form } from 'ant-design-vue';
import { assignObject, timeAgo } from 'ele-admin-pro';
import type { Assets } from '@/api/oa/assets/model';
import { getAssets } from '@/api/oa/assets';
import 'bytemd/dist/index.min.css';
import 'github-markdown-css/github-markdown-light.css';
// // 链接、删除线、复选框、表格等的插件
import gfm from '@bytemd/plugin-gfm';
// // 插件的中文语言文件
import zh_HansGfm from '@bytemd/plugin-gfm/locales/zh_Hans.json';
// 中文语言文件
import 'bytemd/dist/index.min.css';
import highlight from '@bytemd/plugin-highlight-ssr';
import 'highlight.js/styles/default.css';
const useForm = Form.useForm;
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 修改回显的数据
data?: Assets | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 插件
const plugins = ref([
gfm({
locale: zh_HansGfm
}),
highlight()
]);
// 用户信息
const assets = reactive<Assets>({
code: '',
createTime: '',
startTime: '',
endTime: '',
name: '',
brand: undefined,
account: '',
password: '',
configuration: '',
panelAccount: '',
panelPassword: '',
panel: '',
financeAmount: 0,
financeYears: 0,
financeRenew: 0,
financeCustomerName: '',
financeCustomerContact: '',
financeCustomerPhone: '',
brandAccount: '',
brandPassword: '',
openPort: '',
comments: '',
root: 'root',
userId: 0,
companyName: '',
nickname: '',
sortNumber: undefined,
status: undefined,
type: '',
assetsId: 0
});
// 请求状态
const loading = ref(true);
// 是否显示最大化切换按钮
const maxAble = ref(true);
// 选项卡位置
const activeKey = ref('1');
const { resetFields } = useForm(assets);
const users = ref<[]>([]);
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
/* 打开外部链接 */
const openUrl = (record) => {
window.open(record.panel);
};
const query = () => {
const assetsId = Number(props.data?.assetsId);
getAssets(assetsId).then((res) => {
console.log(res);
assets.systemTotal = JSON.parse(res.systemTotal);
});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
if (props.data) {
loading.value = false;
assignObject(assets, props.data);
query();
}
} else {
resetFields();
}
}
);
</script>
<style lang="less">
.tab-pane {
min-height: 100px;
}
.important {
font-weight: bold;
}
</style>
@@ -0,0 +1,65 @@
<!-- 搜索表单 -->
<template>
<a-form
:label-col="{ md: { span: 6 }, sm: { span: 24 } }"
:wrapper-col="{ md: { span: 18 }, sm: { span: 24 } }"
>
<a-row>
<a-col :lg="6" :md="12" :sm="24" :xs="24">
<a-form-item label="服务器名称">
<a-input
v-model:value.trim="where.name"
placeholder="请输入服务器名称"
allow-clear
@pressEnter="search"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24" :xs="24">
<a-form-item label="IP地址">
<a-input
v-model:value.trim="where.code"
placeholder="请输入服务器IP"
allow-clear
@pressEnter="search"
/>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :sm="24" :xs="24">
<a-form-item class="ele-text-right" :wrapper-col="{ span: 24 }">
<a-space>
<a-button type="primary" @click="search">查询</a-button>
<a-button @click="reset">重置</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</template>
<script lang="ts" setup>
import useSearch from '@/utils/use-search';
import type { AssetsParam } from '@/api/oa/assets/model';
const emit = defineEmits<{
(e: 'search', where?: AssetsParam): void;
}>();
// 表单数据
const { where, resetFields } = useSearch<AssetsParam>({
name: '',
code: '',
isExpire: ''
});
/* 搜索 */
const search = () => {
emit('search', where);
};
/* 重置 */
const reset = () => {
resetFields();
search();
};
</script>
@@ -0,0 +1,44 @@
<!-- 选择下拉框 -->
<template>
<a-select
show-search
optionFilterProp="label"
:options="data"
allow-clear
:value="value"
:placeholder="placeholder"
@update:value="updateValue"
@blur="onBlur"
/>
</template>
<script lang="ts" setup>
import { getDictionaryOptions } from '@/utils/common';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'blur'): void;
}>();
withDefaults(
defineProps<{
value?: string;
placeholder?: string;
}>(),
{
placeholder: '请选择服务器厂商'
}
);
// 字典数据
const data = getDictionaryOptions('serverBrand');
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
</script>
@@ -0,0 +1,45 @@
<!-- 选择下拉框 -->
<template>
<a-select
show-search
optionFilterProp="label"
:options="data"
allow-clear
:value="value"
:placeholder="placeholder"
@update:value="updateValue"
@blur="onBlur"
/>
</template>
<script lang="ts" setup>
import { getDictionaryOptions } from '@/utils/common';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'blur'): void;
}>();
withDefaults(
defineProps<{
value?: string;
placeholder?: string;
}>(),
{
placeholder: '请选择服务器配置'
}
);
// 字典数据
const data = getDictionaryOptions('serverConfiguration');
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
</script>
@@ -0,0 +1,70 @@
<!-- 选择下拉框 -->
<template>
<a-select
show-search
optionFilterProp="label"
:options="data"
allow-clear
:value="value"
:placeholder="placeholder"
@update:value="updateValue"
@blur="onBlur"
@search="onSearch"
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { message } from 'ant-design-vue';
import { pageCustomer } from '@/api/oa/customer';
import type { SelectProps } from 'ant-design-vue';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'blur'): void;
}>();
withDefaults(
defineProps<{
value?: number;
placeholder?: string;
}>(),
{
placeholder: '请选择所属客户'
}
);
// 字典数据
const data = ref<SelectProps['options']>([]);
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
};
/* 获取客户列表数据 */
pageCustomer({ progress: '4' })
.then((result) => {
data.value = result?.list.map((d) => {
return {
value: d.customerId,
label: d.customerName
};
});
})
.catch((e) => {
message.error(e.message);
});
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
// 联动搜索
const onSearch = (customerName) => {
pageCustomer({ customerName }).then((res) => {
console.log(res);
});
};
</script>
@@ -0,0 +1,46 @@
<!-- 选择下拉框 -->
<template>
<a-select
mode="multiple"
show-search
optionFilterProp="label"
:options="data"
allow-clear
:value="value"
:placeholder="placeholder"
@update:value="updateValue"
@blur="onBlur"
/>
</template>
<script lang="ts" setup>
import { getDictionaryOptions } from '@/utils/common';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'blur'): void;
}>();
withDefaults(
defineProps<{
value?: string;
placeholder?: string;
}>(),
{
placeholder: '请选择已开发的端口'
}
);
// 字典数据
const data = getDictionaryOptions('serverOpenPort');
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
</script>
@@ -0,0 +1,128 @@
<!-- 搜索表单 -->
<template>
<a-space>
<a-button
type="primary"
class="ele-btn-icon"
v-permission="'oa:assets:save'"
@click="add"
>
<template #icon>
<PlusOutlined />
</template>
<span>新建</span>
</a-button>
<a-button
danger
type="primary"
class="ele-btn-icon"
:v-role="`dev`"
@click="removeBatch"
v-if="props.selection.length > 0"
>
<template #icon>
<DeleteOutlined />
</template>
<span>批量删除</span>
</a-button>
<a-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入搜索关键词"
@search="search"
@pressEnter="search"
>
<template #addonBefore>
<a-select v-model:value="type" style="width: 120px; margin: -5px -12px">
<a-select-option value="name">服务器名称</a-select-option>
<a-select-option value="code">服务器IP</a-select-option>
<a-select-option value="userId">用户ID</a-select-option>
</a-select>
</template>
</a-input-search>
</a-space>
</template>
<script lang="ts" setup>
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
import useSearch from '@/utils/use-search';
import type { AssetsParam } from '@/api/oa/assets/model';
// import SelectUser from '@/components/SelectUser/index.vue';
import { ref, watch } from 'vue';
import { assignObject } from 'ele-admin-pro';
const emit = defineEmits<{
(e: 'search', where?: AssetsParam): void;
(e: 'add'): void;
(e: 'remove'): void;
}>();
const props = defineProps<{
// 勾选的项目
selection?: [];
}>();
// 表单数据
const { where } = useSearch<AssetsParam>({
name: '',
code: '',
isExpire: '',
userId: undefined
});
// 下来选项
const type = ref('name');
// 搜索内容
const searchText = ref('');
/* 搜索 */
const search = () => {
assignObject(where, {});
if (type.value == 'name') {
where.name = searchText.value;
}
if (type.value == 'code') {
where.code = searchText.value;
}
if (type.value == 'userId') {
const number = Number(searchText.value);
if (number > 0) {
where.userId = Number(searchText.value);
}
}
emit('search', where);
};
// const onSelect = (userId) => {
// console.log(userId);
// where.userId = userId;
// console.log(where);
// search();
// };
// const onClear = () => {
// where.userId = undefined;
// search();
// };
// 新增
const add = () => {
emit('add');
};
// 批量删除
const removeBatch = () => {
emit('remove');
};
/* 重置 */
// const reset = () => {
// resetFields();
// search();
// };
// 监听字典id变化
watch(
() => props.selection,
() => {}
);
</script>
@@ -0,0 +1,45 @@
<!-- 角色选择下拉框 -->
<template>
<a-select
show-search
optionFilterProp="label"
:options="data"
allow-clear
:value="value"
:placeholder="placeholder"
@update:value="updateValue"
@blur="onBlur"
/>
</template>
<script lang="ts" setup>
import { getDictionaryOptions } from '@/utils/common';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'blur'): void;
}>();
withDefaults(
defineProps<{
value?: string;
placeholder?: string;
}>(),
{
placeholder: '请选择年限'
}
);
// 字典数据
const data = getDictionaryOptions('serverYears');
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
</script>
+329
View File
@@ -0,0 +1,329 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false">
<!-- 表格 -->
<ele-pro-table
ref="tableRef"
row-key="assetsId"
:columns="columns"
:height="tableHeight"
:datasource="datasource"
v-model:selection="selection"
:customRow="customRow"
:scroll="{ x: 800 }"
>
<template #toolbar>
<search
:selection="selection"
@search="reload"
@add="openEdit"
@remove="removeBatch"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'customer'">
{{ record.nickname }}
<!-- <div class="user-box">-->
<!-- <a-avatar-->
<!-- :size="30"-->
<!-- :src="`${record.avatar}`"-->
<!-- style="margin-right: 4px"-->
<!-- >-->
<!-- <template #icon>-->
<!-- <UserOutlined />-->
<!-- </template>-->
<!-- </a-avatar>-->
<!-- <div class="user-info">-->
<!-- <span>{{ record.companyName }}</span>-->
<!-- <span class="ele-text-placeholder">{{-->
<!-- record.nickname-->
<!-- }}</span>-->
<!-- </div>-->
<!-- </div>-->
</template>
<template v-if="column.key === 'name'">
<a-tooltip title="查看详情">
<a href="#" @click="openInfo(record)">{{ record.name }}</a>
</a-tooltip>
</template>
<template v-if="column.key === 'code'">
<a-tooltip title="服务器公网IP">
{{ record.code }}
</a-tooltip>
</template>
<template v-if="column.key === 'status'">
<a-tag v-if="record.status === '0'" color="green">正常</a-tag>
<a-tag v-if="record.status === '1'" color="red">停机</a-tag>
</template>
<template v-if="column.key === 'comments'">
<a-tooltip :title="record.comments" placement="topLeft">
{{ record.comments }}
</a-tooltip>
</template>
<template v-if="column.key === 'endTime'">
<div class="ele-text-info">{{
toDateString(record.startTime, 'yyyy-MM-dd')
}}</div>
<div v-html="getEndTime(record.endTime)"></div>
</template>
<template v-if="column.key === 'createTime'">
<a-tooltip :title="`${toDateString(record.createTime)}`">
{{ timeAgo(record.createTime) }}
</a-tooltip>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openInfo(record)">详情</a>
<a-divider type="vertical" />
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<assets-edit v-model:visible="showEdit" :data="current" @done="reload" />
<!-- 用户详情 -->
<assets-info v-model:visible="showInfo" :data="current" @done="reload" />
</div>
</div>
</template>
<!--suppress TypeScriptValidateTypes -->
<script lang="ts" setup>
import { timeAgo } from 'ele-admin-pro';
import { createVNode, computed, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import {
UserOutlined,
ExclamationCircleOutlined
} from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import { toDateString } from 'ele-admin-pro';
import Search from './components/search.vue';
import AssetsEdit from './components/assets-edit.vue';
import AssetsInfo from './components/assets-info.vue';
import { pageAssets, removeAssets, removeBatchAssets } from '@/api/oa/assets';
import type { Assets, AssetsParam } from '@/api/oa/assets/model';
import { getDictionaryOptions, getEndTime } from '@/utils/common';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 是否显示资产详情
const showInfo = ref(false);
// 当前用户信息
const brand = getDictionaryOptions('serverBrand');
// 表格列配置
const columns = ref<ColumnItem[]>([
{
key: 'index',
width: 48,
align: 'center',
fixed: 'left',
hideInSetting: true,
customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)
},
{
title: '服务器名称',
dataIndex: 'name',
key: 'name'
},
{
title: '服务器IP',
dataIndex: 'code',
key: 'code'
},
{
title: '品牌',
dataIndex: 'brand',
filters: brand
},
// {
// title: '备注',
// dataIndex: 'comments',
// key: 'comments',
// ellipsis: true
// },
{
title: '有效期',
dataIndex: 'endTime',
key: 'endTime',
sorter: true
},
{
title: '所属客户',
dataIndex: 'customer',
key: 'customer'
},
{
title: '状态',
dataIndex: 'status',
sorter: true,
key: 'status'
},
{
title: '排序',
dataIndex: 'sortNumber',
sorter: true,
hideInTable: true
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
sorter: true,
ellipsis: true,
hideInTable: true,
customRender: ({ text }) => toDateString(text)
},
{
title: '操作',
key: 'action',
width: 200,
align: 'center',
hideInSetting: true
}
]);
// 表格选中数据
const selection = ref<Assets[]>([]);
// 当前编辑数据
const current = ref<Assets | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.brand = filters.brand;
}
return pageAssets({ ...where, ...orders, page, limit });
};
/* 搜索 */
const reload = (where?: AssetsParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
// 搜索是否展开
const searchExpand = ref(false);
// 表格固定高度
const fixedHeight = ref(false);
// 表格高度
const tableHeight = computed(() => {
return fixedHeight.value
? searchExpand.value
? 'calc(100vh - 618px)'
: 'calc(100vh - 562px)'
: void 0;
});
/* 打开编辑弹窗 */
const openEdit = (row?: Assets) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开用户详情弹窗 */
const openInfo = (row?: Assets) => {
current.value = row ?? null;
showInfo.value = true;
};
/* 删除单个 */
const remove = (row: Assets) => {
const hide = message.loading('请求中..', 0);
removeAssets(row.assetsId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchAssets(selection.value.map((d) => d.assetsId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 自定义行属性 */
const customRow = (record: Assets) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openInfo(record);
}
};
};
</script>
<script lang="ts">
export default {
name: 'Assets'
};
</script>
<style lang="less" scoped>
.user-box {
display: flex;
align-items: center;
.user-info {
display: flex;
flex-direction: column;
align-items: start;
}
}
</style>