diff --git a/src/api/common/index.ts b/src/api/common/index.ts index f559403..f0407d4 100644 --- a/src/api/common/index.ts +++ b/src/api/common/index.ts @@ -54,6 +54,10 @@ enum Api { getEnum = '/adminApi/Common/GetSysEnumSelect', getInfoClientSelect = '/adminApi/Common/GetInfoClientSelect', + // 港口下拉数据 + GetClientPortSelectList = '/adminApi/Common/GetPortSelectList', + // 船公司下拉数据 + GetCarrierSelectList = '/adminApi/Common/GetCarrierSelectList', } export function getInfoClientSelect(query: { code: string }) { return request({ @@ -138,13 +142,13 @@ export function getUserInfo() { method: 'get', }) } -export function UserRefreshToken(data) { +export function UserRefreshToken(data) { return request>({ url: Api.UserRefreshToken, method: 'get', - headers:{ - RefreshToken:data.refreshToken - } + headers: { + RefreshToken: data.refreshToken, + }, }) } export function userLogout() { @@ -284,7 +288,20 @@ export function changePassword(data: any) { data, }) } - +// 港口下拉数据列表 +export function GetClientPortSelectList() { + return request({ + url: Api.GetClientPortSelectList, + method: 'get', + }) +} +// 船公司下拉 +export function GetCarrierSelectList() { + return request({ + url: Api.GetCarrierSelectList, + method: 'get', + }) +} /** * 上传api路径 */ diff --git a/src/components/Form/src/components/FormItem.vue b/src/components/Form/src/components/FormItem.vue index 8ce579a..f3e9a90 100644 --- a/src/components/Form/src/components/FormItem.vue +++ b/src/components/Form/src/components/FormItem.vue @@ -1,18 +1,18 @@ diff --git a/src/components/Form/src/types/form.ts b/src/components/Form/src/types/form.ts index 7bb5ce9..e6e4eaa 100644 --- a/src/components/Form/src/types/form.ts +++ b/src/components/Form/src/types/form.ts @@ -1,227 +1,235 @@ -import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface'; -import type { VNode } from 'vue'; -import type { ButtonProps as AntdButtonProps } from '/@/components/Button'; -import type { FormItem } from './formItem'; -import type { ColEx, ComponentType } from './index'; -import type { TableActionType } from '/@/components/Table/src/types/table'; -import type { CSSProperties } from 'vue'; -import type { RowProps } from 'ant-design-vue/lib/grid/Row'; +import type { NamePath, RuleObject } from 'ant-design-vue/lib/form/interface' +import type { VNode } from 'vue' +import type { ButtonProps as AntdButtonProps } from '/@/components/Button' +import type { FormItem } from './formItem' +import type { ColEx, ComponentType } from './index' +import type { TableActionType } from '/@/components/Table/src/types/table' +import type { CSSProperties } from 'vue' +import type { RowProps } from 'ant-design-vue/lib/grid/Row' -export type FieldMapToTime = [string, [string, string], (string | [string, string])?][]; +export type FieldMapToTime = [string, [string, string], (string | [string, string])?][] export type Rule = RuleObject & { - trigger?: 'blur' | 'change' | ['change', 'blur']; -}; + trigger?: 'blur' | 'change' | ['change', 'blur'] +} export interface RenderCallbackParams { - schema: FormSchema; - values: Recordable; - model: Recordable; - field: string; + schema: FormSchema + values: Recordable + model: Recordable + field: string } export interface ButtonProps extends AntdButtonProps { - text?: string; + text?: string } export interface FormActionType { - submit: () => Promise; - setFieldsValue: (values: T) => Promise; - resetFields: () => Promise; - getFieldsValue: () => Recordable; - clearValidate: (name?: string | string[]) => Promise; - updateSchema: (data: Partial | Partial[]) => Promise; - resetSchema: (data: Partial | Partial[]) => Promise; - setProps: (formProps: Partial) => Promise; - removeSchemaByField: (field: string | string[]) => Promise; + submit: () => Promise + setFieldsValue: (values: T) => Promise + resetFields: () => Promise + getFieldsValue: () => Recordable + clearValidate: (name?: string | string[]) => Promise + updateSchema: (data: Partial | Partial[]) => Promise + resetSchema: (data: Partial | Partial[]) => Promise + setProps: (formProps: Partial) => Promise + removeSchemaByField: (field: string | string[]) => Promise appendSchemaByField: ( schema: FormSchema | FormSchema[], prefixField: string | undefined, first?: boolean | undefined, - ) => Promise; - validateFields: (nameList?: NamePath[]) => Promise; - validate: (nameList?: NamePath[]) => Promise; - scrollToField: (name: NamePath, options?: ScrollOptions) => Promise; + ) => Promise + validateFields: (nameList?: NamePath[]) => Promise + validate: (nameList?: NamePath[]) => Promise + getFieldList: (nameList?: NamePath[]) => Promise + scrollToField: (name: NamePath, options?: ScrollOptions) => Promise + linkageForm: () => Promise } -export type RegisterFn = (formInstance: FormActionType) => void; +export type RegisterFn = (formInstance: FormActionType) => void -export type UseFormReturnType = [RegisterFn, FormActionType]; +export type UseFormReturnType = [RegisterFn, FormActionType] export interface FormProps { - name?: string; - layout?: 'vertical' | 'inline' | 'horizontal'; + name?: string + layout?: 'vertical' | 'inline' | 'horizontal' // Form value - model?: Recordable; + model?: Recordable // The width of all items in the entire form - labelWidth?: number | string; + labelWidth?: number | string // alignment - labelAlign?: 'left' | 'right'; + labelAlign?: 'left' | 'right' // Row configuration for the entire form - rowProps?: RowProps; + rowProps?: RowProps // Submit form on reset - submitOnReset?: boolean; + submitOnReset?: boolean // Submit form on form changing - submitOnChange?: boolean; + submitOnChange?: boolean // Col configuration for the entire form - labelCol?: Partial; + labelCol?: Partial // Col configuration for the entire form - wrapperCol?: Partial; + wrapperCol?: Partial // General row style - baseRowStyle?: CSSProperties; + baseRowStyle?: CSSProperties // General col configuration - baseColProps?: Partial; + baseColProps?: Partial // Form configuration rules - schemas?: FormSchema[]; + schemas?: FormSchema[] // Function values used to merge into dynamic control form items - mergeDynamicData?: Recordable; + mergeDynamicData?: Recordable // Compact mode for search forms - compact?: boolean; + compact?: boolean // Blank line span - emptySpan?: number | Partial; + emptySpan?: number | Partial // Internal component size of the form - size?: 'default' | 'small' | 'large'; + size?: 'default' | 'small' | 'large' // Whether to disable - disabled?: boolean; + disabled?: boolean // Time interval fields are mapped into multiple - fieldMapToTime?: FieldMapToTime; + fieldMapToTime?: FieldMapToTime // Placeholder is set automatically - autoSetPlaceHolder?: boolean; + autoSetPlaceHolder?: boolean // Auto submit on press enter on input - autoSubmitOnEnter?: boolean; + autoSubmitOnEnter?: boolean // Check whether the information is added to the label - rulesMessageJoinLabel?: boolean; + rulesMessageJoinLabel?: boolean // Whether to show collapse and expand buttons - showAdvancedButton?: boolean; + showAdvancedButton?: boolean // Whether to focus on the first input box, only works when the first form item is input - autoFocusFirstItem?: boolean; + autoFocusFirstItem?: boolean // Automatically collapse over the specified number of rows - autoAdvancedLine?: number; + autoAdvancedLine?: number // Always show lines - alwaysShowLines?: number; + alwaysShowLines?: number // Whether to show the operation button - showActionButtonGroup?: boolean; - + showActionButtonGroup?: boolean + // 是否使用高级查询 + useAdvancedSearch?: boolean + // 是否使用多单号查询 + useNumbersSearch?: boolean // Reset button configuration - resetButtonOptions?: Partial; + resetButtonOptions?: Partial // Confirm button configuration - submitButtonOptions?: Partial; + submitButtonOptions?: Partial // Operation column configuration - actionColOptions?: Partial; + actionColOptions?: Partial // Show reset button - showResetButton?: boolean; + showResetButton?: boolean // Show confirmation button - showSubmitButton?: boolean; + showSubmitButton?: boolean - resetFunc?: () => Promise; - submitFunc?: () => Promise; - transformDateFunc?: (date: any) => string; - colon?: boolean; + resetFunc?: () => Promise + submitFunc?: () => Promise + transformDateFunc?: (date: any) => string + colon?: boolean } export interface FormSchema { // Field name - field: string; + field: string // Event name triggered by internal value change, default change - changeEvent?: string; + changeEvent?: string // Variable name bound to v-model Default value - valueField?: string; + valueField?: string // Label name - label: string | VNode; + label?: string | VNode + enLabel?: string | VNode // Auxiliary text - subLabel?: string; + subLabel?: string // Help text on the right side of the text helpMessage?: | string | string[] - | ((renderCallbackParams: RenderCallbackParams) => string | string[]); + | ((renderCallbackParams: RenderCallbackParams) => string | string[]) // BaseHelp component props - helpComponentProps?: Partial; + helpComponentProps?: Partial // Label width, if it is passed, the labelCol and WrapperCol configured by itemProps will be invalid - labelWidth?: string | number; + labelWidth?: string | number // Disable the adjustment of labelWidth with global settings of formModel, and manually set labelCol and wrapperCol by yourself - disabledLabelWidth?: boolean; + disabledLabelWidth?: boolean // render component - component: ComponentType; + component: ComponentType // Component parameters componentProps?: | ((opt: { - schema: FormSchema; - tableAction: TableActionType; - formActionType: FormActionType; - formModel: Recordable; + schema: FormSchema + tableAction: TableActionType + formActionType: FormActionType + formModel: Recordable }) => Recordable) - | object; + | object // Required - required?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean); + required?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) - suffix?: string | number | ((values: RenderCallbackParams) => string | number); + suffix?: string | number | ((values: RenderCallbackParams) => string | number) // Validation rules - rules?: Rule[]; + rules?: Rule[] // Check whether the information is added to the label - rulesMessageJoinLabel?: boolean; + rulesMessageJoinLabel?: boolean // Reference formModelItem - itemProps?: Partial; + itemProps?: Partial // col configuration outside formModelItem - colProps?: Partial; + colProps?: Partial // 默认值 - defaultValue?: any; + defaultValue?: any // 是否自动处理与时间相关组件的默认值 - isHandleDateDefaultValue?: boolean; + isHandleDateDefaultValue?: boolean - isAdvanced?: boolean; + isAdvanced?: boolean // Matching details components - span?: number; + span?: number - ifShow?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean); + ifShow?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) - show?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean); + show?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) // Render the content in the form-item tag - render?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string; + render?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string // Rendering col content requires outer wrapper form-item - renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string; + renderColContent?: (renderCallbackParams: RenderCallbackParams) => VNode | VNode[] | string renderComponentContent?: | ((renderCallbackParams: RenderCallbackParams) => any) | VNode | VNode[] - | string; + | string // Custom slot, in from-item - slot?: string; + slot?: string // Custom slot, similar to renderColContent - colSlot?: string; + colSlot?: string + // label上的插槽 + labelSlot?: string - dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean); + dynamicDisabled?: boolean | ((renderCallbackParams: RenderCallbackParams) => boolean) - dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[]; + dynamicRules?: (renderCallbackParams: RenderCallbackParams) => Rule[] } export interface HelpComponentProps { - maxWidth: string; + maxWidth: string // Whether to display the serial number - showIndex: boolean; + showIndex: boolean // Text list - text: any; + text: any // colour - color: string; + color: string // font size - fontSize: string; - icon: string; - absolute: boolean; + fontSize: string + icon: string + absolute: boolean // Positioning - position: any; + position: any } diff --git a/src/hooks/Permissions/index.ts b/src/hooks/Permissions/index.ts new file mode 100644 index 0000000..54aec9a --- /dev/null +++ b/src/hooks/Permissions/index.ts @@ -0,0 +1,7 @@ +import { intersection } from 'lodash-es' +import { usePermissionStore } from '/@/store/modules/permission' +export function checkPermissions(key) { + const permissionStore = usePermissionStore() + const allCodeList = permissionStore.getPermCodeList as string[] + return !((intersection([key], allCodeList) as string[]).length > 0) +} \ No newline at end of file diff --git a/src/hooks/web/common.ts b/src/hooks/web/common.ts new file mode 100644 index 0000000..df76a6f --- /dev/null +++ b/src/hooks/web/common.ts @@ -0,0 +1,224 @@ +import { router } from "/@/router" +import { useMultipleTabStore } from '/@/store/modules/multipleTab' +import { useMessage } from '/@/hooks/web/useMessage' +// 获取表单更改过的字段接口 +import { getFormSetInfoByModule } from '/@/views/baseinfo/formset/api' +// 引入表格权限信息(包含id和name) +import { permissionsInfo } from '/@/hooks/web/usePermission' +import * as XLSX from 'xlsx' +const { createMessage } = useMessage() +// 格式表格时间 +export function formatTableData(v) { + if (v) { + v = v.split(' ')[0] + if (v.includes('1900')) v = '' + return v + } else { + return '' + } +} + +/* 关闭当前标签页 (默认关闭当前标签页面返回上一级路由) + * topath: 指定跳转的path +*/ +export function closePage(topath) { + const tabStore = useMultipleTabStore() + tabStore.closeTab(router.currentRoute._value, router) + // 有目标路径则跳转 + if (topath) { + router.push(topath) + } else { + // 父级路由 + const parent = router?.options?.history?.state?.back + // 如果存在父级路由,直接返回父级 + if (parent) { + router.push(parent) + } else { + // 否则返回上一级路由 + router.back() + } + } +} + +/* 根据接口和过滤条件导出excel(目前支持单表头表格导出) + * api: 接口,params: 过滤条件,columes: 表格列,name: excel名称 +*/ +export function exportExcel(api, params = {}, columns = [], name = '未命名') { + if (!api) return createMessage.warning('缺少api') + params['pageCondition']['isExport'] = true + // 过滤出需要显示的列 + const cols = columns.filter(item => { + return !item.defaultHidden || item.visible + }) + api(params).then(res => { + const { data } = res + const fileData = data.map(row => { + const obj = {} + cols.forEach(item => { + for (var key in row) { + if (item.dataIndex === key) { + obj[item.title] = row[key] + } + } + }) + return obj + }) + toBlob(fileData, name) + }) +} + +// 将字符串转换为ArrayBuffer +function s2ab(s) { + const buf = new ArrayBuffer(s.length) + const view = new Uint8Array(buf) + for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff + return buf +} + +/* 根据数据直接导出excel(目前支持单表头表格导出) + * data: 表格数据,columes: 表格列,name: excel名称 +*/ +export function exportExcelByData(data = [], columns = [], name = '未命名') { + // 过滤出需要显示的列 + const cols = columns.filter(item => { + return !item.defaultHidden || item.visible + }) + const fileData = data.map(row => { + const obj = {} + cols.forEach(item => { + for (var key in row) { + if (item.dataIndex === key) { + obj[item.title] = row[key] + } + } + }) + return obj + }) + toBlob(fileData, name) +} + +function toBlob(fileData, name) { + // 创建工作簿 + const workbook = XLSX.utils.book_new() + // 将数据转换为工作表 + const worksheet = XLSX.utils.json_to_sheet(fileData) + // 将工作表添加到工作簿 + XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1') + // 生成Excel的配置 + const wbout = XLSX.write(workbook, { bookType: 'xlsx', type: 'binary' }) + // 创建二进制对象并创建url + const blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' }) + const url = URL.createObjectURL(blob) + // 创建a标签模拟点击进行下载 + const a = document.createElement('a') + a.href = url + a.download = name + '.xlsx' + document.body.appendChild(a) + a.click() + // 清除对象URL + setTimeout(() => { + URL.revokeObjectURL(url) + document.body.removeChild(a) + }, 0) +} + +// 更新表单数据 +export function updateFormItem(updateSchema, formNo) { + getFormSetInfoByModule({ permissionId: permissionsInfo().permissionId, formNo }).then(res => { + if (res?.data?.content) { + const content = JSON.parse(res.data.content) + console.log(content) + updateSchema(content.columns) + } + }) +} + +// 返回表格查询的参数 multipleList(需要多选查询的字段名数组) +export function formatParams(params = {}) { + const postData = { + queryCondition: '', + pageCondition: { + pageIndex: params?.page, + pageSize: params?.pageSize, + sortConditions: [] + } + } + const conditions = [] as any + for (let key in params) { + // 排除不是分页排序等字段 + if (key != 'page' && key != 'pageSize' && key != 'order' && key != 'field') { + if (key == 'advancedSearchParams') { + // 高级查询 + conditions.push(params[key]) + } else { + // 其他查询 + if (Array.isArray(params[key])) { + const isDate = new Date(params[key][0]) as any + // 判定为日期 + if (isDate != 'Invalid Date' && isDate.getFullYear() > 1000) { + //替换时分秒 查询用 + params[key][0] = params[key][0].substring(0, 11) + "00:00:00"; + params[key][1] = params[key][1].substring(0, 11) + "23:59:59"; + conditions.push({ + FieldName: key, + FieldValue: params[key][0], + ConditionalType: 3 + }) + conditions.push({ + FieldName: key, + FieldValue: params[key][1], + ConditionalType: 5 + }) + } else { + // 判定为筛选 + params[key].forEach(item => { + conditions.push({ + FieldName: key, + FieldValue: item, + ConditionalType: 1 + }) + }) + } + } else if (key.includes(':')) { + // or 多个字段查询 + const orKeys = key.split(':') + const ConditionalList = { ConditionalList: [] } + if (params[key]) { + orKeys.forEach(item => { + let obj = { + Key: 1, + Value: { + FieldName: item, + FieldValue: params[key], + ConditionalType: 1 + } + } + ConditionalList.ConditionalList.push(obj) + }) + conditions.push(ConditionalList) + } + } else { + // 其他普通查询 + if (params[key] || params[key] === 0) { + conditions.push({ + FieldName: key, + FieldValue: params[key], + ConditionalType: 1 + }) + } + } + } + } + } + // 排序 + if (params?.field) { + postData.pageCondition.sortConditions = [{ + sortField: params.field, + listSortDirection: params.order == "ascend" ? 0 : 1 + }] + } else { + postData.pageCondition.sortConditions = [] + } + postData.queryCondition = JSON.stringify(conditions) + return postData +} diff --git a/src/views/baseinfo/ReleaseType/TenantAuditStepModal.vue b/src/views/baseinfo/ReleaseType/TenantAuditStepModal.vue new file mode 100644 index 0000000..e47a6f3 --- /dev/null +++ b/src/views/baseinfo/ReleaseType/TenantAuditStepModal.vue @@ -0,0 +1,123 @@ + + diff --git a/src/views/baseinfo/ReleaseType/api.ts b/src/views/baseinfo/ReleaseType/api.ts new file mode 100644 index 0000000..a6e26a4 --- /dev/null +++ b/src/views/baseinfo/ReleaseType/api.ts @@ -0,0 +1,40 @@ +// @ts-ignore +import { request } from '/@/utils/request' +import { DataResult, PageRequest } from '/@/api/model/baseModel' +enum Api { + list = '/adminApi/ReleaseType/GetList', + editORinfo = '/adminApi/ReleaseType/Edit', + del = '/adminApi/ReleaseType/Delete', +} +// 列表 (Auth) +export function ApiList(data: PageRequest) { + return request({ + url: Api.list, + method: 'post', + data, + }) +} +// 编辑 (Auth) +export function ApiEdit(data: PageRequest) { + return request({ + url: Api.editORinfo, + method: 'post', + data, + }) +} +// 详情 (Auth) +export function ApiInfo(query) { + return request({ + url: Api.editORinfo, + method: 'get', + params: query, + }) +} +// 批量删除 (Auth) +export function ApiDel(data: PageRequest) { + return request({ + url: Api.del, + method: 'post', + data, + }) +} \ No newline at end of file diff --git a/src/views/baseinfo/ReleaseType/columns.tsx b/src/views/baseinfo/ReleaseType/columns.tsx new file mode 100644 index 0000000..eb634c6 --- /dev/null +++ b/src/views/baseinfo/ReleaseType/columns.tsx @@ -0,0 +1,170 @@ +import { BasicColumn, FormSchema } from '/@/components/Table' +import { GetCarrierSelectList, GetClientPortSelectList } from '/@/api/common' +export const columns: BasicColumn[] = [ + { + title: '承运商', + dataIndex: 'carrier', + sorter: true, + width: 150, + }, + { + title: '起运港代码', + dataIndex: 'loadingPort', + sorter: true, + width: 200, + }, + { + title: '放舱方式名称', + dataIndex: 'releaseTypeName', + sorter: true, + width: 200, + }, +] + +export const searchFormSchema: FormSchema[] = [ + { + label: '承运商', + field: 'carrier', + labelSlot: 'carrierId', + component: 'ApiSelect', + colProps: { span: 6 }, + componentProps: ({ formModel }) => { + return { + api: GetCarrierSelectList, + labelField: 'pinYinCode', + showName: 'cnName', + valueField: 'code', + resultField: 'data', + immediate: false, + onChange: (e, obj) => { + console.log(obj) + + if (e && obj) { + formModel.carrierId = obj.id + } + if (!e && !obj) { + formModel.carrierId = '' + } + }, + } + }, + }, + { + label: '起运港', + field: 'loadingPort', + component: 'ApiSelect', + colProps: { span: 6 }, + componentProps: () => { + return { + api: GetClientPortSelectList, + resultField: 'data', + allowClear: true, + showSearch: true, + labelField: 'portName', + showName: 'portName', + valueField: 'ediCode', + immediate: true, + filterOption: (input: string, option: any) => { + return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }, + } + }, + }, + { + field: 'releaseType', + label: '放舱方式', + component: 'Select', + + colProps: { span: 6 }, + componentProps: { + options: [ + { label: '入货通知', value: 1 }, + { label: '转发BC', value: 2 }, + ], + }, + }, +] +export const formSchema: FormSchema[] = [ + { + label: '', + field: 'id', + component: 'Input', + defaultValue: '', + show: false, + }, + { + label: '承运商', + field: 'carrier', + labelSlot: 'carrierId', + component: 'ApiSelect', + required: false, + dynamicDisabled: false, + colProps: { span: 20 }, + componentProps: ({ formModel }) => { + return { + api: GetCarrierSelectList, + labelField: 'pinYinCode', + showName: 'cnName', + valueField: 'code', + resultField: 'data', + immediate: false, + filterOption: (input: string, option: any) => { + return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }, + onChange: (e, obj) => { + console.log(obj) + + if (e && obj) { + formModel.carrierId = obj.id + } + if (!e && !obj) { + formModel.carrierId = '' + } + }, + } + }, + }, + { + label: '委托单位', + field: 'customerId', + component: 'Input', + defaultValue: null, + show: false, + }, + { + label: '起运港', + field: 'loadingPort', + component: 'ApiSelect', + required: true, + dynamicDisabled: false, + colProps: { span: 20 }, + componentProps: () => { + return { + api: GetClientPortSelectList, + resultField: 'data', + allowClear: true, + showSearch: true, + labelField: 'portName', + showName: 'portName', + valueField: 'ediCode', + immediate: true, + filterOption: (input: string, option: any) => { + return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 + }, + } + }, + }, + { + field: 'releaseType', + label: '放舱方式', + component: 'Select', + required: true, + colProps: { span: 20 }, + componentProps: { + options: [ + { label: '入货通知', value: 1 }, + { label: '转发BC', value: 2 }, + ], + }, + }, +] diff --git a/src/views/baseinfo/ReleaseType/index.vue b/src/views/baseinfo/ReleaseType/index.vue new file mode 100644 index 0000000..f06cad3 --- /dev/null +++ b/src/views/baseinfo/ReleaseType/index.vue @@ -0,0 +1,150 @@ + +