实列;vue3动态组件表单,表格组件 增删改查。 需要更好处理数据 ,自己修改后可分享大家交流学习
动态表单组件form.vue
<template>
<!-- 动态组件,可以是 div、a-modal、a-drawer 等 -->
<component :is="toComponent.name" v-bind="toComponent.props">
<!-- 表单容器 -->
<a-form ref="formRef" :rules="rules" :model="modelValue" auto-label-width scroll-to-first-error>
<!-- 表单布局行 -->
<a-row :gutter="gutter">
<!-- 动态生成表单项 -->
<a-col v-for="item in items" :key="getKey(item)" :span="item.span || span">
<a-form-item :label="item.label || item.title" :field="getKey(item)">
<!-- 动态表单项组件 -->
<component :is="getComponent(item)" v-bind="getProps(item)" v-model="modelValue[getKey(item)]">
<!-- 动态插槽 -->
<template v-for="slot in item.slots" :key="slot.name" #[slot.name]>
<div v-html="slot.value"></div>
</template>
</component>
<!-- 帮助文本插槽 -->
<template v-if="item.help" #help>{{ item.help }}</template>
</a-form-item>
</a-col>
<!-- 操作按钮区域(仅在非弹窗模式下显示) -->
<a-col v-if="component === 'div'" :span="span">
<slot>
<!-- 默认按钮插槽 -->
<component :is="okText === '搜索' ? 'div' : 'a-form-item'">
<a-space size="medium">
<a-button type="primary" :loading="okLoading" @click="submit">
{{ okText }}
</a-button>
<a-button @click="reset">重置</a-button>
</a-space>
</component>
</slot>
</a-col>
</a-row>
<!-- 底部自定义插槽 -->
<slot name="footer"></slot>
</a-form>
</component>
</template>
<script setup>
import { ref, computed } from 'vue'
// 定义组件事件
const emits = defineEmits(['submit', 'reset']);
// 双向绑定的表单数据
const modelValue = defineModel()
// 组件属性定义
const props = defineProps({
formItems: {
type: Array,
default: [] // 表单项配置数组
},
formRules: {
type: Object,
default: null // 表单验证规则
},
defaultValues: {
type: Object,
default: null // 默认值
},
gutter: {
type: Number,
default: 0 // 栅格间隔
},
span: {
type: Number,
default: 24 // 表单项默认占据的栅格数
},
okText: {
type: String,
default: '保存' // 确认按钮文本
},
component: {
type: [String, Object],
default: 'div' // 包裹表单的组件类型
}
})
// 表单项类型到实际组件的映射
const componentMap = {
input: 'a-input', // 输入框
number: 'a-input-number', // 数字输入框
password: 'a-input-password', // 密码框
radio: 'a-radio-group', // 单选框组
checkbox: 'a-checkbox-group', // 多选框组
textarea: 'a-textarea', // 文本域
select: 'a-select', // 选择器
treeSelect: 'a-tree-select', // 树形选择
rangePicker: 'a-range-picker', // 时间范围选择器
datePicker: 'a-date-picker', // 日期选择器
timePicker: 'a-time-picker', // 时间选择器
slider: 'a-slider', // 滑动输入条
switch: 'a-switch', // 开关
editor: 'ai-editor', // 富文本编辑器
icon: 'ai-icon', // 图标选择器
upfile: 'ai-upload-file', // 文件上传
upimage: 'ai-upload-image', // 图片上传
dict: 'g-dict', // 字典选择器
}
// 表单引用
const formRef = ref(null)
// 过滤掉隐藏的表单项
const items = computed(() => props.formItems.filter(m => !m.hidden && m.is_edit !== false))
// 生成表单验证规则
const rules = computed(() => {
if (props.formRules) return props.formRules
return items.value.reduce((res, m) => {
if (m?.is_required === true) {
res[getKey(m)] = [{
required: true,
message: (m.label || m.title) + '不能为空'
}]
}
return res
}, {})
})
// 获取表单当前值
const values = () => {
return items.value.reduce((res, m) => {
res[getKey(m)] = m?.value !== null ? m?.value : undefined
return res
}, {})
}
/**
* 根据表单项配置获取对应的组件
*/
const getComponent = ({ view_type, type }) => {
const component = view_type || type
if (component && typeof componentMap[component] === 'string') {
return componentMap[component]
}
return 'a-input'
}
/**
* 获取表单项的props配置
*/
const getProps = (item) => {
if (typeof item.props === 'object') return item.props
return {}
}
/**
* 获取表单项的标识key
*/
const getKey = (item) => {
return item.key ?? item.field
}
/**
* 获取表单默认值
*/
const getDefaultValues = () => {
const values = {}
props.formItems.forEach(m => {
let key = getKey(m)
values[key] = props.defaultValues?.[key] ?? m.default_value ?? m.value
})
return values
}
// 弹窗组件相关状态(用于 modal/drawer 模式)
const popup = ref({
name: 'a-modal',
props: {
visible: false, // 显示/隐藏
okLoading: false, // 确认按钮loading状态
placement: 'right', // 抽屉位置
unmountOnClose: true,// 关闭时卸载组件
onOk: () => { // 确认回调
submit()
},
onCancel: () => { // 取消回调
if (popup.value.props?.cancelText === '重置') {
reset() // 如果取消按钮文本是"重置",则执行重置
} else {
close() // 否则关闭弹窗
}
},
}
})
/**
* 计算最终要渲染的组件
*/
const toComponent = computed(() => {
if (typeof props.component === 'string') {
if (['a-modal', 'a-drawer'].includes(props.component)) {
// 如果是弹窗组件,返回popup配置
popup.value.name = props.component
return popup.value
}
return { name: props.component }
} else {
// 直接返回组件对象
return props.component
}
})
/**
* 控制loading状态
*/
const okLoading = ref(false)
const load = (bool = true, timer = 1500) => {
okLoading.value = bool
popup.value.props.okLoading = bool
// 自动关闭loading(模拟请求完成)
bool && setTimeout(() => {
okLoading.value = false
popup.value.props.okLoading = false
}, timer)
}
/**
* 表单提交方法
*/
const submit = async (callback) => {
// 表单验证
const error = await formRef.value?.validate()
if (error) return
// 显示loading状态
load()
// 执行回调或触发submit事件
if (typeof callback === 'function') {
callback(modelValue.value)
} else {
emits('submit', modelValue.value)
}
}
/**
* 表单重置方法
*/
const reset = (callback) => {
modelValue.value = { ...getDefaultValues() }
// 执行回调或触发reset事件
if (typeof callback === 'function') {
callback(modelValue.value)
} else {
emits('reset', modelValue.value)
}
}
/**
* 打开弹窗
*/
const open = (props, visible = true) => {
Object.assign(popup.value.props, { ...props, visible })
}
/**
* 关闭弹窗
*/
const close = (props, visible = false) => {
load(false)
Object.assign(popup.value.props, { ...props, visible })
}
// 暴露给父组件的方法
defineExpose({
load, // 控制loading
open, // 打开弹窗
close, // 关闭弹窗
submit, // 提交表单
reset, // 重置表单
values, // 获取默认值
validate: (...args) => { // 表单验证
return formRef.value.validate(...args)
},
})
</script>
表格组件table.vue 增删改查
<template>
<a-layout ref="layoutRef" class="a-layout">
<!-- 搜索框 -->
<slot name="search" :show="search">
<a-card v-if="search" ref="searchRef" class="a-search">
<g-from v-if="form" ref="formRef" ok-text="搜索" v-model="searchData" :form-items="searchItems" :gutter="15"
:span="6" @submit="onSearch" @reset="onReset" />
</a-card>
</slot>
<a-layout class="a-table">
<!-- 操作按钮 -->
<slot name="button" :keys="selectedKeys">
<g-button v-if="mode?.operate !== 0" :buttons="buttons" :expandAll="expandAll" :hasSearch="search"
:hasChildren="children" @operate="onOperate" @search="onSearchShow" @expand="onExpandAll"
@refresh="onRefresh" />
</slot>
<!-- 表格列表 -->
<a-col :style="{ x: '100%', height: scroll.y + 'px', flex: '1' }">
<a-table :rowKey="rowKey" :data="datas" :scroll="scroll" :loading="loading" v-model:selectedKeys="selectedKeys"
:expandedKeys="expandedKeys" :rowSelection="rowSelection" :pagination="pagination" :draggable="draggable"
:bordered="bordered" stickyHeader striped columnResizable @expandedChange="onExpanded"
@pageChange="pageChange" @pageSizeChange="pageSizeChange" @change="tableChange">
<template #columns>
<template v-for="row in columns" :key="row[colKey]">
<a-table-column v-if="row.is_list !== false" :data-index="row[colKey]" :width="row.width"
:title="row.title || row.label" :fixed="row.fixed" :tooltip="row.tooltip ?? true" ellipsis
align="center">
<template #cell="{ record, rowIndex }">
<slot name="cell" :record="record" :rowIndex="rowIndex" :rowKey="rowKey" :colKey="colKey"
:column="row">
<!-- 可选操作 -->
<slot name="link" v-if="row[colKey] === 'optional'">
<g-link :rowKey="rowKey" :colKey="colKey" :data="record" :column="row" :links="buttons"
@operate="onOptional($event, record)" />
</slot>
<!-- 显示数据 -->
<slot v-else name="show">
<g-show :rowKey="rowKey" :colKey="colKey" :data="record" :column="row" @change="handleUpdate" />
</slot>
</slot>
</template>
</a-table-column>
</template>
</template>
</a-table>
</a-col>
</a-layout>
<!-- 表单模态框 -->
<g-from v-if="form" ref="formRef" v-model="formData" :form-items="formItems" :form-rules="rules"
:component="component" />
<!-- 骨架屏 -->
<g-skeleton v-if="skeleton" />
</a-layout>
</template>
<script setup>
import { ref, computed, watch } from 'vue'
import gButton from './components/button.vue';
import gLink from './components/link.vue';
import gShow from './components/show.vue';
import gSkeleton from './components/skeleton.vue';
import { Message } from '@arco-design/web-vue';
const emits = defineEmits([
'operate',
'query',
'selected',
'change',
])
const props = defineProps({
rowKey: { type: String, default: 'id' },
colKey: { type: String, default: 'field' },
data: { type: Array, default: [] },
columns: { type: Array, default: [] },
pagination: { type: Object, default: null },
draggable: { type: Object, default: null },
rowSelection: { type: Object, default: null },
bordered: { type: Object, default: { cell: true } },
buttons: { type: Array, default: [] },
mode: { type: Object, default: {} },
skeleton: { type: Boolean, default: false },
form: { type: [Boolean, Object], default: false },
rules: { type: Object, default: null },
api: { type: Object, default: null },
})
const datas = ref([]);
const columns = ref([]);
const queryParams = ref({});
const selectedKeys = ref([]);
const expandedKeys = ref([]);
const expandAll = ref(false);
const search = ref(false);
const children = ref(false);
const loading = ref(false);
const layoutRef = ref(null)
const rowSelection = computed(() => {
return props.rowSelection ? {
type: 'checkbox',
columnWidth: 40,
showCheckedAll: true,
onlyCurrent: true,
fixed: true,
...props.rowSelection
} : null
})
const pagination = computed({
get: () => {
return props.pagination ? {
current: 1, // 当前页码
pageSize: 20, // 每页显示条数
total: 0, // 数据总数
size: "small", // 分页器大小
showTotal: true, // 显示总数统计
showJumper: true, // 显示页码跳转
showPageSize: true, // 显示页容量切换
...props.pagination
} : false
},
set: (val) => {
props.pagination = val
}
})
let tht = 0, sht = 0;
const scroll = computed(() => {
tht = tht > 0 ? tht : layoutRef.value?.$el?.offsetHeight - 120;
sht = sht > 0 ? sht : searchRef.value?.$el?.clientHeight + 12;
let y = tht > 0 ? tht : 0;
if (search.value) {
y -= sht > 0 ? sht : 0;
}
if (pagination.value) {
y -= 40;
}
return { x: '100%', y: y }
})
//-------------------------------监听事件----------------------------------
// 列参数变化
watch(() => props.columns, (val) => {
columns.value = val
search.value = val.some(m => m.is_query === true)
}, { immediate: true, deep: true })
// 表格数据变化
watch(() => props.data, (val) => {
datas.value = val
children.value = val.some(m => m.children?.length)
selectedKeys.value = []
loading.value = false
}, { deep: true })
// 选中变化
watch(() => selectedKeys.value, (val) => {
emits('selected', val)
})
// 模式变化
watch(() => props.mode, (val) => {
const bool = columns.value.some(m => m[props.colKey] === 'optional')
if (!bool && val.optional !== 0) {
columns.value.push({
[props.colKey]: 'optional',
title: '操作',
fixed: 'right',
tooltip: false,
width: val?.colWidth ?? 150,
is_list: Boolean(val?.optional ?? true),
})
}
}, { immediate: true, deep: true })
//-------------------------------处理数据----------------------------------
// 查询数据
const handleQuery = () => {
loading.value = true
emits('query', {
page: pagination?.value?.current,
pageSize: pagination?.value?.pageSize,
...queryParams.value
})
}
// 更新处理
const handleUpdate = (data) => {
if (data.id?.length == 0) {
Message.error('请选择要更新的数据')
return
}
props.api.update(data).then(() => handleQuery())
}
// 删除处理
const handleDelete = (data) => {
if (data.id?.length == 0) {
Message.error('请选择要删除的数据')
return
}
props.api.delete(data).then(() => handleQuery())
}
//-------------------------------表格事件----------------------------------
const searchRef = ref(null)
const searchData = ref({})
const searchItems = computed(() => {
return props.columns.filter(m => m.is_query === true)
.map(m => ({
key: m[props.colKey],
label: m.label ?? m.title,
type: m.view_type,
value: m?.value,
rules: m.rules,
props: m.props ?? {},
}))
})
// 搜索数据
const onSearch = (data) => {
queryParams.value = { ...data };
if (pagination.value !== false) {
pagination.value.current = 1
}
handleQuery()
}
// 重置搜索
const onReset = () => {
onSearch()
}
// 刷新数据
const onRefresh = () => {
queryParams.value = {}
if (pagination.value !== false) {
pagination.value.current = 1
}
handleQuery()
}
// 显示搜索
const onSearchShow = (val) => {
search.value = val
}
// 展开状态
const onExpanded = (Keys) => {
expandedKeys.value = [...Keys]
}
// 展开控制
const onExpandAll = () => {
loading.value = !expandAll.value
setTimeout(() => {
expandedKeys.value = expandAll.value && expandedKeys.value.length
? [] : rowKeys(props.data)
expandAll.value = expandedKeys.value.length > 0
loading.value = false
}, 100)
}
// 获取所有行key
const rowKeys = (data) => {
let res = [];
data.forEach(m => {
res.push(m[props.rowKey])
if (m.children) res = res.concat(rowKeys(m.children))
});
return res
}
// 表格按钮
const onOperate = ({ key, data }) => {
if (!props.api) {
emits('operate', { key, data })
return;
}
const id = { id: selectedKeys.value }
switch (key) {
case 'add':
openForm(key)
break
case 'clear':
handleDelete(id)
break
default:
if (data?.[key]) {
handleUpdate({ ...data, ...id })
} else {
emits('operate', { key, data })
}
}
}
// 可选操作
const onOptional = ({ key, data }, record = null) => {
if (!props.api) {
emits('operate', { key, data, record })
return;
}
const id = { id: record?.[props.rowKey] }
switch (key) {
case 'save':
openForm(key, record)
break
case 'delete':
handleDelete(id)
break
default:
if (data?.[key]) {
handleUpdate({ ...data, ...id })
} else {
emits('operate', { key, data, record })
}
}
}
//-------------------------------表格数据变化----------------------------------
// 分页变化
const pageChange = (current) => {
if (pagination.value.current === current) return;
pagination.value.current = current;
handleQuery()
}
// 页数量大小变化
const pageSizeChange = (size) => {
if (pagination.value.pageSize === size) return;
pagination.value.pageSize = size;
handleQuery()
}
// 数据变化
const tableChange = (data) => {
emits('change', data)
}
//-------------------------------表单事件----------------------------------
const action = ref('')
const formRef = ref(null)
const formData = ref({});
const formItems = computed(() => {
return props.columns.filter(m => m[props.colKey] !== 'optional' && m.is_edit !== false)
.map(m => ({
key: m[props.colKey],
label: m.label ?? m.title,
type: m.view_type,
value: m?.value,
is_edit: m.is_edit,
is_required: m.is_required,
help: m?.help,
rules: m.rules,
props: m.props ?? {},
}))
})
// 表单组件
const component = ref({
name: 'a-modal',
props: {
visible: false,
okLoading: false,
maskClosable: false,
unmountOnClose: true,
onOk: () => {
formRef.value?.submit(data => {
component.value.props.okLoading = true
props.api[action.value]({ ...data, ...props.form?.data })
.then(() => {
Object.assign(component.value.props, {
visible: false,
okLoading: false
})
handleQuery()
}).catch(err => {
console.log(err)
}).finally(() => {
component.value.props.okLoading = false
})
})
},
onCancel: () => {
Object.assign(component.value.props, {
visible: false,
okLoading: false
})
},
}
})
// 打开表单
const openForm = (key, data) => {
action.value = key;
formRef.value?.reset()
formData.value = data ?? formRef.value?.values()
component.value.name = props.mode?.module || 'a-modal'
Object.assign(component.value.props, {
width: props.mode?.modalWidth,
fullscreen: Boolean(props.mode?.fullscreen),
title: key === 'add' ? '新增' : '编辑',
visible: true
})
}
defineExpose()
</script>
<style lang="scss" scoped>
.a-layout {
height: 100%;
position: relative;
overflow: hidden;
}
.a-table {
padding: 16px;
background: var(--color-bg-2);
border-radius: var(--border-radius-small);
box-sizing: border-box;
overflow: hidden auto;
}
.a-search {
margin-bottom: 12px;
border: none;
:deep(.arco-card-body) {
padding: 16px 16px 0;
}
:deep(.arco-form-item),
:deep(.arco-space-item) {
margin-bottom: 16px;
}
}
</style>
列数据结构
columns=[
{
"key": "PRI",
"sort": 1,
"type": "int",
"field": "id",
"label": "主键",
"value": null,
"width": 180,
"is_edit": false,
"is_json": false,
"is_list": false,
"is_query": true,
"is_insert": true,
"view_type": null,
"query_type": null,
"is_required": false
},
{
"key": "MUL",
"sort": 2,
"type": "int",
"field": "user_id",
"label": "用户ID",
"value": null,
"width": 180,
"is_edit": false,
"is_json": false,
"is_list": false,
"is_query": false,
"is_insert": true,
"view_type": null,
"query_type": null,
"is_required": false
},
{
"key": "",
"sort": 3,
"type": "varchar",
"field": "title",
"label": "标题",
"props": {
"allowClear": true,
"placeholder": "请输入标题"
},
"value": "",
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": true,
"is_query": false,
"is_insert": true,
"view_type": "input",
"query_type": null,
"is_required": false
},
{
"key": "",
"sort": 4,
"type": "varchar",
"field": "cover",
"label": "封面",
"props": {
"limit": 1,
"multiple": false
},
"value": "",
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": true,
"is_query": false,
"is_insert": true,
"view_type": "upimage",
"query_type": null,
"extend_code": null,
"extend_type": null,
"is_required": false
},
{
"key": "MUL",
"sort": 5,
"type": "int",
"field": "category_id",
"label": "栏目分类",
"props": {
"fieldNames": {
"key": "cid",
"title": "name",
"children": "children"
},
"options": [
{
"cid": 1,
"name": "栏目一"
},
{
"cid": 2,
"name": "栏目二"
}
]
},
"value": null,
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": true,
"is_query": true,
"is_insert": true,
"view_type": "treeSelect",
"query_type": "eq",
"extend_code": "category",
"extend_type": "category",
"is_required": false
},
{
"key": "",
"sort": 6,
"type": "enum",
"field": "type",
"label": "发布类型",
"props": {
"fieldNames": {
"label": "label",
"value": "value"
},
"options": [
{
"label": "文章",
"value": "text"
},
{
"label": "视频",
"value": "video"
},
{
"label": "图片",
"value": "image"
}
]
},
"value": null,
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": true,
"is_query": false,
"is_insert": true,
"view_type": "radio",
"query_type": null,
"extend_code": "article_type",
"extend_type": "dict",
"is_required": false
},
{
"key": "",
"sort": 7,
"type": "varchar",
"field": "describe",
"label": "简介",
"props": [],
"value": "",
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": true,
"is_query": false,
"is_insert": true,
"view_type": "textarea",
"query_type": null,
"is_required": false
},
{
"key": "",
"sort": 8,
"type": "mediumtext",
"field": "content",
"label": "内容",
"props": [],
"value": null,
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": false,
"is_query": false,
"is_insert": true,
"view_type": "editor",
"query_type": null,
"extend_code": null,
"extend_type": null,
"is_required": false
},
{
"key": "",
"sort": 9,
"type": "varchar",
"field": "images",
"label": "图片数组",
"value": null,
"width": 180,
"is_edit": false,
"is_json": false,
"is_list": false,
"is_query": false,
"is_insert": true,
"view_type": null,
"query_type": null,
"is_required": false
},
{
"key": "",
"sort": 10,
"type": "varchar",
"field": "video",
"label": "视频地址",
"value": "",
"width": 180,
"is_edit": false,
"is_json": false,
"is_list": false,
"is_query": false,
"is_insert": true,
"view_type": null,
"query_type": null,
"is_required": false
},
{
"key": "",
"sort": 11,
"type": "tinyint",
"field": "vip_level",
"label": "访问权限",
"props": {
"allowClear": true,
"fieldNames": {
"label": "label",
"value": "value"
},
"placeholder": "请选择会员",
"options": [
{
"label": "普通会员",
"value": 0
},
{
"label": "黄金会员",
"value": 1
},
{
"label": "砖石会员",
"value": 2
},
{
"label": "尊贵会员",
"value": 3
}
]
},
"value": null,
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": true,
"is_query": false,
"is_insert": true,
"view_type": "select",
"query_type": null,
"extend_code": "vip_level",
"extend_type": "dict",
"is_required": false
},
{
"key": "",
"sort": 15,
"type": "tinyint",
"field": "is_push",
"label": "是否推荐",
"props": {
"fieldNames": {
"label": "label",
"value": "value"
},
"options": [
{
"label": "否",
"value": 0
},
{
"label": "是",
"value": 1
}
]
},
"value": 0,
"width": 180,
"is_edit": true,
"is_json": false,
"is_list": true,
"is_query": false,
"is_insert": true,
"view_type": "radio",
"query_type": null,
"extend_code": "yes_status",
"extend_type": "dict",
"is_required": false
},
]
封装axios request.js
import axios from 'axios'
import router from '@/router'
import { Message } from '@arco-design/web-vue'
const message = (code, msg) => {
if (!msg?.length) return;
const msgType = {
200: 'info',
201: 'success',
204: 'warning',
401: 'warning',
500: 'error',
}
Message[msgType[code] || 'error'](msg);
if (code === 401) {
localStorage.removeItem('Token');
setTimeout(() => {
router.push({ name: 'login' })
}, 1500);
}
}
const baseURL = import.meta.env.PROD ? import.meta.env.VITE_API_BASE_URL : '/api';
const service = axios.create({ baseURL: baseURL, timeout: 5000 })
service.interceptors.request.use(rec => {
rec.headers.Authorization = localStorage.getItem('Token');
return rec;
})
service.interceptors.response.use(res => {
const { code, data, msg } = res.data;
message(code, msg);
if (data !== undefined) {
return data;
}
}, err => {
message(500, '网络请求异常,请稍后重试');
return Promise.reject(err);
})
const request = (options) => {
options.method = options.method || 'get';
if (options.method.toLowerCase() === 'get') {
options.params = options.data;
}
const onUploadProgress = options.onUploadProgress;
if (onUploadProgress && typeof onUploadProgress === 'function') {
options.onUploadProgress = onUploadProgress;
}
return service(options);
}
['get', 'post', 'put', 'delete'].forEach(key => {
request[key] = (url, data, options) => {
return request({
url,
data,
method: key,
...options,
})
}
})
request.Token = () => localStorage.getItem('Token')
request.baseURL = baseURL
export default request;
封装api.js
import request from '@/utils/request'
const methods = {
get: 'GET',
post: 'POST',
list: 'GET',
read: 'GET',
add: 'POST',
save: 'PUT',
update: 'PUT',
delete: 'DELETE'
}
const $api = {
get: (action, data, options = {}) => request({
url: action,
method: 'GET',
data,
...options,
}),
post: (action, data, options = {}) => request({
url: action,
method: 'POST',
data,
...options,
}),
upload: (data, onProgress = () => { }, options = {}) => request({
url: 'upload',
data,
method: 'POST',
onUploadProgress: onProgress,
...options,
}),
URL: request.baseURL,
Token: request.Token,
}
$api.set = (obj) => {
for (let k in obj) {
if ($api[k]) {
$api.clear(k)
}
$api[k] = {}
for (let m in methods) {
if (['get', 'post'].includes(m)) {
$api[k][m] = (action, data, options) => request({
url: `${obj[k]}/${action}`,
method: methods[m],
data,
...options,
})
} else {
$api[k][m] = (data, options) => request({
url: `${obj[k]}/${m}`,
method: methods[m],
data,
...options,
})
}
}
}
return $api
}
$api.clear = (keys) => {
if (!keys) {
for (let k in $api) {
if (['get', 'post', 'upload'].includes(k)) {
continue
}
delete $api[k]
}
return
}
else if (typeof keys === 'string') {
if ($api[keys]) {
delete $api[keys]
}
return
}
keys.forEach(k => {
if ($api[k]) {
delete $api[k]
}
})
}
export default $api
main.js
import { createApp } from 'vue';
import arco from '@arco-design/web-vue';
import arcoIcon from '@arco-design/web-vue/es/icon';
import ueditor from 'vue-ueditor-wrap';
import components from '@/components/web-vue';
import store from './store';
import router from './router';
import api from '@/utils/api'
import App from './App.vue';
import './style.scss';
import '@arco-design/web-vue/dist/arco.css';
const app = createApp(App);
app.provide('api', api);
app.use(router)
.use(store)
.use(arco)
.use(arcoIcon)
.use(ueditor)
.use(components)
.mount('#app');
组件 引用
import { ref, inject, onMounted } from 'vue'
const $api = inject('api').set({ menu: 'system.menu' })
不需要 卸载组件同时 删除
onUnmounted(() => {
$api.clear('menu')
})
1823

被折叠的 条评论
为什么被折叠?



