feat: 优化上传组件
自动部署 / build (push) Successful in 1m40s Details

master
luoer 2023-11-23 17:10:25 +08:00
parent e5bf0bfb8b
commit 261f2490ec
11 changed files with 306 additions and 254 deletions

View File

@ -3,14 +3,14 @@ import { TableColumn } from '../hooks/useTableColumn';
export function useUpdateColumn(extra: TableColumn = {}): TableColumn {
return {
title: '更新用户',
title: '更新',
dataIndex: 'createdAt',
width: 190,
width: 180,
render: ({ record }) => (
<div class="flex flex-col overflow-hidden">
<span>{record.updatedBy ?? '无'}</span>
<span class="text-gray-400 text-xs truncate">
{dayjs(record.updatedAt).format()}
<span class="text-gray-400 text-xs truncate" title={record.updatedAt}>
{dayjs(record.updatedAt).fromNow()}
</span>
</div>
),
@ -20,14 +20,14 @@ export function useUpdateColumn(extra: TableColumn = {}): TableColumn {
export function useCreateColumn(extra: TableColumn = {}): TableColumn {
return {
title: '创建用户',
title: '作者',
dataIndex: 'createdAt',
width: 190,
width: 180,
render: ({ record }) => (
<div class="flex flex-col overflow-hidden">
<span>{record.createdBy ?? '无'}</span>
<span class="text-gray-400 text-xs truncate">
{dayjs(record.createdAt).format()}
<span class="text-gray-400 text-xs truncate" title={record.createdAt}>
{dayjs(record.createdAt).fromNow()}
</span>
</div>
),

View File

@ -153,7 +153,7 @@ function useTableButtonColumn(column: TableButtonColumn & TableColumnData) {
}
return (
<>
{index !== 0 && <Divider direction="vertical" margin={4} />}
{index !== 0 && <Divider direction="vertical" margin={2} />}
<Link {...item.buttonProps} disabled={item.disable?.(props)} onClick={() => item.onClick?.(props)}>
{item.text}
</Link>

View File

@ -2,6 +2,7 @@ import { delConfirm, delOptions } from '@/utils';
import { AnTableContext } from '../components/Table';
import { AnTablePlugin } from '../hooks/useTablePlugin';
import { Message } from '@arco-design/web-vue';
import { defaultsDeep } from 'lodash-es';
export function useRowDelete(): AnTablePlugin {
let ctx: AnTableContext;
@ -19,6 +20,11 @@ export function useRowDelete(): AnTablePlugin {
if (!btn) {
continue;
}
defaultsDeep(btn, {
buttonProps: {
status: 'danger',
},
});
const onClick = btn.onClick;
btn.onClick = async props => {
let confirm = btn.confirm ?? {};

View File

@ -6,15 +6,15 @@
上传
</a-button>
<a-modal
v-model:visible="visible"
title="上传文件"
title-align="start"
v-model:visible="visible"
:width="940"
:mask-closable="false"
:on-before-cancel="onBeforeCancel"
@close="onClose"
>
<div class="mb-2 flex items-center gap-4">
<div class="flex items-center gap-4 py-0">
<a-upload
ref="uploadRef"
class="upload"
@ -40,43 +40,55 @@
<ul v-if="fileList.length" class="h-[424px] overflow-hidden p-0 m-0">
<a-scrollbar outer-class="h-full overflow-hidden" class="h-full overflow-auto pr-[20px] divide-y">
<li v-for="item in fileList" :key="item.uid" class="flex items-center gap-2 py-3">
<li v-for="item in fileList" :key="item.uid" class="flex items-center gap-4 py-3">
<div class="text-4xl rounded pr-0.5 flex justify-center">
<i :class="getIcon(item.file?.type ?? 'video')"></i>
</div>
<div class="flex-1 overflow-hidden">
<div class="truncate text-slate-900">
{{ item.name }}
<div class="h-8 truncate text-slate-900 flex justify-between items-center gap-2">
<div>
{{ item.name }}
<span class="text-xs text-gray-400 ml-2">{{ numeral(item.file?.size).format('0 b') }}</span>
</div>
<div v-show="item.status !== 'done'">
<a-link v-show="item.status === 'uploading'" @click="pauseItem(item)"></a-link>
<a-link v-show="item.status === 'error'" @click="retryItem(item)"></a-link>
<a-link v-show="item.status === 'init' || item.status === 'error'" @click="removeItem(item)">
删除
</a-link>
</div>
</div>
<div class="flex items-center justify-between gap-2 text-gray-400 mb-[-4px] mt-0.5">
<span class="text-xs text-gray-400">
{{ numeral(item.file?.size).format('0 b') }}
</span>
<a-progress :percent="formatProgress(item, true)" :show-text="false" class="block!"></a-progress>
<div class="flex items-center justify-between gap-2 text-gray-400 mt-1.5 text-xs">
<span class="text-xs">
<span v-if="item.status === 'init'"> </span>
<span v-else-if="item.status === 'uploading'">
<span class="text-xs">
速度{{ numeral(fileMap.get(item.uid)?.speed || 0).format('0 b') }}/s, 进度{{
Math.floor((item.percent || 0) * 100)
}}%
</span>
<span v-if="item.status === 'init'">
<i class="icon-park-outline-hourglass-full"></i>
等待上传
</span>
<span v-else-if="item.status === 'done'" class="text-green-600">
完成( 耗时{{ fileMap.get(item.uid)?.cost || 0 }}, 平均{{
numeral(fileMap.get(item.uid)?.aspeed || 0).format('0 b')
}}/s)
<span v-else-if="item.status === 'uploading'" class="text-[rgb(var(--primary-6))]">
<i class="icon-park-outline-upload-one"></i>
正在上传
</span>
<span v-else-if="item.status === 'done'" class="text-[rgb(var(--success-6))]">
<i class="icon-park-outline-check"></i>
上传成功
</span>
<span v-else="item.status === 'error'" class="text-red-500">
失败(原因{{ fileMap.get(item.uid)?.error }})
<i class="icon-park-outline-close"></i>
上传失败
</span>
</span>
<span>
<span v-if="item.status === 'init'"> </span>
<span v-else-if="item.status === 'uploading'">
速度{{ formatSpeed(item.uid) }}/s, 进度{{ formatProgress(item) }} %
</span>
<span v-else-if="item.status === 'done'">
耗时{{ fileMap.get(item.uid)?.cost || 0 }} , 平均{{ formatAspeed(item.uid) }}/s
</span>
<span v-else="item.status === 'error'"> {{ fileMap.get(item.uid)?.error }} </span>
</span>
</div>
<a-progress :percent="Math.floor((item.percent || 0) * 100) / 100" :show-text="false"></a-progress>
</div>
<div v-show="item.status !== 'done'">
<a-link v-show="item.status === 'uploading'" @click="pauseItem(item)"></a-link>
<a-link v-show="item.status === 'error'" @click="retryItem(item)"></a-link>
<a-link v-show="item.status === 'init' || item.status === 'error'" @click="removeItem(item)"></a-link>
</div>
</li>
</a-scrollbar>
@ -132,9 +144,20 @@ const fileMap = reactive<
>
>(new Map());
/**
* 统计信息
*/
const formatProgress = (item: FileItem, small?: boolean) => {
let percent = Math.floor((item.percent || 0) * 100);
percent = percent < 100 ? percent : item.response ? percent : 99;
return small ? percent / 100 : percent;
};
const formatSpeed = (uid: string) => {
return numeral(fileMap.get(uid)?.speed || 0).format('0 b');
};
const formatAspeed = (uid: string) => {
return numeral(fileMap.get(uid)?.aspeed || 0).format('0 b');
};
const stat = computed(() => {
const result = {
initCount: 0,
@ -151,17 +174,10 @@ const stat = computed(() => {
return result;
});
/**
* 开始上传
*/
const startUpload = () => {
uploadRef.value?.submit();
};
/**
* 中止上传
* @param item 文件
*/
const pauseItem = (item: FileItem) => {
uploadRef.value?.abort(item);
const file = fileMap.get(item.uid);
@ -170,10 +186,6 @@ const pauseItem = (item: FileItem) => {
}
};
/**
* 移除文件
* @param item 文件
*/
const removeItem = (item: FileItem) => {
const index = fileList.value.findIndex(i => i.uid === item.uid);
if (index > -1) {
@ -181,17 +193,10 @@ const removeItem = (item: FileItem) => {
}
};
/**
* 重新上传
* @param item 文件
*/
const retryItem = (item: FileItem) => {
uploadRef.value?.submit(item);
};
/**
* 清空已上传
*/
const clearUploaded = async () => {
if (stat.value.doneCount !== fileList.value.length) {
await delConfirm('当前有未上传完成的文件,是否继续清空?');
@ -199,18 +204,10 @@ const clearUploaded = async () => {
fileList.value = [];
};
/**
* 上传成功后处理
* @param item 文件
*/
const onUploadSuccess = (item: FileItem) => {
emit('success', item);
};
/**
* 上传失败后处理
* @param item 文件
*/
const onUploadError = (item: FileItem) => {
const file = fileMap.get(item.uid);
if (file) {
@ -218,9 +215,6 @@ const onUploadError = (item: FileItem) => {
}
};
/**
* 关闭前检测
*/
const onBeforeCancel = () => {
if (fileList.value.some(i => i.status === 'uploading')) {
Message.warning('提示:文件上传中,请稍后再试!');
@ -229,19 +223,12 @@ const onBeforeCancel = () => {
return true;
};
/**
* 关闭后处理
*/
const onClose = () => {
fileMap.clear();
fileList.value = [];
emit('close', stat.value.doneCount);
};
/**
* 自定义上传逻辑
* @param option
*/
const upload = (option: RequestOption) => {
const { fileItem, onError, onProgress, onSuccess } = option;
const source = axios.CancelToken.source();
@ -257,26 +244,26 @@ const upload = (option: RequestOption) => {
}
const item = fileMap.get(fileItem.uid)!;
const startTime = Date.now();
const data = { file: fileItem.file as any };
const params: RequestParams = {
onUploadProgress(e) {
let percent = 0;
const { lastTime, lastLoaded } = item;
if (e.total && e.total > 0) {
percent = e.loaded / e.total;
const nowTime = Date.now();
const diff = (e.loaded - lastLoaded) / (nowTime - lastTime);
const speed = Math.floor(diff * 1000);
item.aspeed = (item.speed + speed) / 2;
item.speed = speed;
item.lastLoaded = e.loaded;
item.lastTime = nowTime;
}
onProgress(percent, e as any);
},
cancelToken: source.token,
};
const up = async () => {
const data = { file: fileItem.file as any };
const params: RequestParams = {
onUploadProgress(e) {
let percent = 0;
const { lastTime, lastLoaded } = item;
if (e.total && e.total > 0) {
percent = e.loaded / e.total;
const nowTime = Date.now();
const diff = (e.loaded - lastLoaded) / (nowTime - lastTime);
const speed = Math.floor(diff * 1000);
item.aspeed = (item.speed + speed) / 2;
item.speed = speed;
item.lastLoaded = e.loaded;
item.lastTime = nowTime;
}
onProgress(percent, e as any);
},
cancelToken: source.token,
};
try {
const res = await api.file.addFile(data, params);
const currentTime = Date.now();

View File

@ -18,11 +18,12 @@
<script setup lang="tsx">
import { FileCategory, api } from '@/api';
import { useCreateColumn, useTable, useUpdateColumn } from '@/components/AnTable';
import { useCreateColumn, useTable, useTableDelete, useUpdateColumn } from '@/components/AnTable';
import { getIcon } from './components/util';
import numeral from 'numeral';
import AnGroup from './components/AnGroup.vue';
import AnUpload from './components/AnUpload.vue';
import { Message } from '@arco-design/web-vue';
const visible = ref(false);
const current = ref<FileCategory>();
@ -45,17 +46,23 @@ const onCategoryChange = (category: FileCategory) => {
tableRef.value?.refresh();
};
const copyLink = (record: Recordable) => {
window.navigator.clipboard.writeText(record.path);
Message.success(`提示:已复制 ${record.name} 的地址!`);
};
const {
component: MaterialTable,
tableRef,
props,
} = useTable({
plugins: [useTableDelete()],
columns: [
{
title: '文件名称',
dataIndex: 'name',
render: ({ record }) => (
<div class="flex items-center gap-2">
<div class="group flex items-center gap-2">
<div class="w-8 flex justify-center">
{record.mimetype.startsWith('image') ? (
<a-avatar size={26} shape="square">
@ -66,16 +73,24 @@ const {
)}
</div>
<div class="flex flex-col overflow-hidden">
<span
class="hover:text-brand-500 hover:decoration-underline underline-offset-2 cursor-pointer"
onClick={() => preview(record)}
>
{record.name}
</span>
<span class="text-gray-400 text-xs truncate">
{numeral(record.size).format('0 b')}
<span class="ml-2">{record.category?.name}</span>
<span class="flex items-center gap-2">
<span
class="truncate hover:text-brand-500 hover:decoration-underline underline-offset-2 cursor-pointer"
onClick={() => preview(record)}
>
{record.name}
</span>
<span class="hidden group-hover:inline text-xs text-gray-400 ml-0" title="复制地址" onClick={() => copyLink(record)}>
<i class=" icon-park-outline-copy hover:text-gray-700 cursor-pointer"></i>
</span>
</span>
<div class="h-5 inline-flex items-center text-xs text-gray-400 space-x-4">
<span>
<i class="icon-park-outline-folder-close mr-1"></i>
{record.category || '默认分类'}
</span>
<span>{numeral(record.size).format('0 b')}</span>
</div>
</div>
</div>
),
@ -103,6 +118,9 @@ const {
onClick: props => {
return api.file.delFile(props.record.id);
},
buttonProps: {
status: 'danger'
}
},
],
},
@ -158,7 +176,7 @@ const {
<route lang="json">
{
"meta": {
"sort": 10305,
"sort": 10300,
"title": "素材管理",
"icon": "icon-park-outline-movie-board"
}

View File

@ -1,63 +1,102 @@
<template>
<div>
<div class="bg-white px-4 pt-2">
<bread-crumb></bread-crumb>
<div class="flex justify-between items-end gap-4 bg-white px-1 py-3">
<div>
<div class="text-lg font-semibold">新增文章</div>
<div class="text-gray-400 mt-1.5">新增的文章需审核才能展现</div>
</div>
<div>
<a-button class="mr-2">保存为草稿</a-button>
<a-button type="primary">保存发布</a-button>
</div>
</div>
</div>
<div class="flex gap-4">
<div class="flex-1 bg-white p-4">
<a-form :model="{}" layout="vertical">
<a-form-item label="标题">
<a-input placeholder="请输入标题" :max-length="120" :show-word-limit="true"></a-input>
</a-form-item>
<a-form-item label="文章内容">
<a-textarea placeholder="说点啥" :max-length="1000" :show-word-limit="true"></a-textarea>
</a-form-item>
</a-form>
</div>
<div class="w-64 bg-white p-4">
<a-form :model="{}" layout="vertical">
<a-form-item label="别名">
<a-input placeholder="请输入"></a-input>
<template #help>
用作URL的别名, 只能包含字母数字下划线和破折号
</template>
</a-form-item>
<a-form-item label="分类">
<a-checkbox-group direction="vertical">
<a-checkbox>开发工具</a-checkbox>
<a-checkbox>日常记录</a-checkbox>
<a-checkbox>心得体验</a-checkbox>
</a-checkbox-group>
</a-form-item>
<a-form-item label="封面图">
<a-upload draggable></a-upload>
</a-form-item>
</a-form>
</div>
</div>
</div>
<BreadPage>
<CategoryTable />
</BreadPage>
</template>
<script setup lang="tsx" name="PostPage">
</script>
<script setup lang="tsx">
import { api } from '@/api';
import { useCreateColumn, useTable, useUpdateColumn } from '@/components/AnTable';
import { listToTree } from '@/utils/listToTree';
<style lang="less">
.export-form {
.arco-form-item-content {
display: block;
}
}
</style>
const { component: CategoryTable } = useTable({
columns: [
{
title: '名称',
dataIndex: 'title',
width: 240,
render: ({ record }) => (
<div class="flex flex-col overflow-hidden">
<span>{record.title}</span>
<span class="text-gray-400 text-xs truncate">#{record.slug}</span>
</div>
),
},
{
title: '描述',
dataIndex: 'description',
},
useCreateColumn(),
useUpdateColumn(),
{
type: 'button',
title: '操作',
width: 120,
buttons: [
{
type: 'modify',
text: '修改',
},
{
type: 'delete',
text: '删除',
onClick({ record }) {
return api.category.delCategory(record.id);
},
},
],
},
],
source: async model => {
const res = await api.category.getCategories(model);
const data = listToTree(res.data.data ?? []);
return { data: { data, total: (res.data as any).total } };
},
search: [
{
field: 'nickname',
label: '登陆账号',
setter: 'search',
enterable: true,
searchable: true,
},
],
create: {
title: '添加分类',
width: 580,
items: [
{
field: 'title',
label: '分类名称',
setter: 'input',
required: true,
},
{
field: 'slug',
label: '分类别名',
setter: 'input',
required: true,
},
{
field: 'description',
label: '描述',
setter: 'textarea',
required: false,
},
],
submit: model => {
return api.category.addCategory(model as any);
},
},
modify: {
extend: true,
title: '修改分类',
submit: model => {
return api.category.setCategory(model.id, model as any);
},
},
});
</script>
<route lang="json">
{

View File

@ -39,7 +39,7 @@ const { component: DictTable, tableRef } = useTable({
<div>
<div>
{record.name}
<span class="text-gray-400 ml-2 text-xs">{record.code}</span>
<span class="text-gray-400 ml-2 text-xs">@{record.code}</span>
</div>
<div class="text-gray-400 text-xs">{record.description}</div>
</div>

View File

@ -1,52 +1,64 @@
<template>
<BreadPage>
<Table v-bind="table">
<LoginLogTable>
<template #action>
<a-button type="primary" @click="visible = true">添加</a-button>
<ani-editor v-model:visible="visible"></ani-editor>
</template>
</Table>
</LoginLogTable>
</BreadPage>
</template>
<script setup lang="tsx">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { Editor as aniEditor } from "@/components/editor";
import dayjs from "dayjs";
defineOptions({ name: "SystemLoglPage" })
import { api } from '@/api';
import { useTable } from '@/components/AnTable';
import { Editor as aniEditor } from '@/components/editor';
import { TableColumnData } from '@arco-design/web-vue';
import dayjs from 'dayjs';
defineOptions({ name: 'SystemLoglPage' });
const visible = ref(false);
const table = useTable({
data: async (model, paging) => {
return api.log.getLoginLogs({ ...model, ...paging });
const useTwoRowsColumn = (tkey: string, bkey: string): TableColumnData['render'] => {
return ({ record }) => {
return (
<div class="flex flex-col overflow-hidden">
<span>{record[tkey] || '未知'}</span>
<span class="text-gray-400 text-xs truncate">{record[bkey]}</span>
</div>
);
};
};
const { component: LoginLogTable } = useTable({
source: async model => {
return api.log.getLoginLogs(model);
},
columns: [
{
title: "登陆账号",
dataIndex: "nickname",
width: 200,
title: '登陆账号',
dataIndex: 'nickname',
width: 140,
render({ record }) {
return (
<div class="flex flex-col overflow-hidden">
<div class="overflow-hidden">
<i class="icon-park-outline-user mr-2"></i>
<span>{record.nickname}</span>
<span class="text-gray-400 text-xs truncate">{dayjs(record.createdAt).format()}</span>
</div>
);
},
},
{
title: "操作描述",
dataIndex: "description",
title: '操作描述',
dataIndex: 'description',
render: ({ record: { status, description } }) => {
return (
<span>
<span
class={
status === null || status
? "text-base text-green-500 icon-park-outline-check-one mr-2"
: "text-base text-red-500 icon-park-outline-close-one mr-2"
? 'text-base text-green-500 icon-park-outline-check-one mr-2'
: 'text-base text-red-500 icon-park-outline-close-one mr-2'
}
></span>
{description}
@ -55,74 +67,69 @@ const table = useTable({
},
},
{
title: "登陆地址",
dataIndex: "ip",
title: '登陆地址',
dataIndex: 'ip',
width: 200,
render({ record }) {
return (
<div class="flex flex-col overflow-hidden">
<span>{record.addr || "未知"}</span>
<span class="text-gray-400 text-xs truncate">{record.ip}</span>
</div>
);
},
render: useTwoRowsColumn('addr', 'ip'),
},
{
title: "操作系统",
dataIndex: "os",
title: '操作系统',
dataIndex: 'os',
width: 200,
render({ record }) {
const [os, version] = record.os.split(" ");
const [os, version] = record.os.split(' ');
return (
<div class="flex flex-col overflow-hidden">
<span>{os || "未知"}</span>
<span>{os || '未知'}</span>
<span class="text-gray-400 text-xs truncate">{version}</span>
</div>
);
},
},
{
title: "浏览器",
dataIndex: "browser",
title: '浏览器',
dataIndex: 'browser',
width: 200,
render({ record }) {
const [browser, version] = record.browser.split(" ");
const [browser, version] = record.browser.split(' ');
return (
<div class="flex flex-col overflow-hidden">
<span>{browser || "未知"}</span>
<span>{browser || '未知'}</span>
<span class="text-gray-400 text-xs truncate">v{version}</span>
</div>
);
},
},
{
title: '登陆时间',
dataIndex: 'createAt',
width: 200,
render({ record }) {
return (
<div class="flex flex-col overflow-hidden">
<span>{dayjs(record.createdAt).fromNow()}</span>
<span class="text-gray-400 text-xs truncate">{dayjs(record.createdAt).format('YYYY-MM-DD HH:mm:ss')}</span>
</div>
);
},
},
],
search: {
button: true,
items: [
{
field: "[startDate, endDate]",
label: "登陆账号",
type: "dateRange",
required: false,
nodeProps: {
field: '[startDate, endDate]',
label: '登陆账号',
setter: 'dateRange',
setterProps: {
placeholder: ['开始时间', '结束时间'],
showTime: true,
timePickerProps: { defaultValue: ["23:59:59", "00:00:00"] },
},
itemProps: {
hideLabel: true,
timePickerProps: { defaultValue: ['23:59:59', '00:00:00'] },
},
},
{
field: "nickname",
label: "登陆账号",
type: "input",
required: false,
nodeProps: {
placeholder: "请输入登陆账号",
},
itemProps: {
hideLabel: true,
},
field: 'nickname',
label: '登陆账号',
setter: 'input',
},
],
},

View File

@ -1,9 +1,9 @@
import { api } from "@/api";
import { useAniFormModal } from "@/components";
import { api } from '@/api';
import { useAniFormModal } from '@/components';
export const usePassworModal = () => {
return useAniFormModal({
title: "重置密码",
title: '重置密码',
trigger: false,
modalProps: {
width: 432,
@ -14,13 +14,9 @@ export const usePassworModal = () => {
},
items: [
{
field: "password",
label: ({ model }) => (
<span>
{model.nickname} :
</span>
),
type: "input",
field: 'password',
label: ({ model }) => `${model.nickname} 的新密码:`,
type: 'input',
},
],
submit: async ({ model }) => {

View File

@ -22,22 +22,29 @@ const { component: UserTable } = useTable({
title: '用户昵称',
dataIndex: 'username',
render: ({ record }) => (
<div class="flex items-center">
<div class="flex items-center gap-4 w-full overflow-hidden">
<a-avatar size={32} class="!bg-brand-500">
{record.avatar?.startsWith('/') ? <img src={record.avatar} alt="" /> : record.nickname?.[0]}
</a-avatar>
<span class="ml-2 flex-1 flex flex-col overflow-hidden">
<span>{record.nickname}</span>
<span class="text-gray-400 text-xs truncate">@{record.username}</span>
</span>
<div class="w-full flex-1 overflow-hidden">
<div>
<span>{record.nickname}</span>
<span class="text-gray-400 text-xs truncate ml-2">@{record.username}</span>
</div>
<div class="w-full text-gray-400 space-x-4 text-xs">
<span>
<i class="icon-park-outline-mail mr-1 align-[-3px]"></i>
contact@juetan.cn
</span>
<span>
<i class="icon-park-outline-phone-telephone mr-1"></i>
1591234568
</span>
</div>
</div>
</div>
),
},
{
title: '用户邮箱',
dataIndex: 'email',
width: 200,
},
useCreateColumn(),
useUpdateColumn(),
{
@ -46,15 +53,15 @@ const { component: UserTable } = useTable({
width: 200,
buttons: [
{
type: 'modify',
text: '修改',
},
{
text: '设置密码',
text: '重置密码',
onClick({ record }) {
passCtx.open(record);
},
},
{
type: 'modify',
text: '修改',
},
{
type: 'delete',
text: '删除',

View File

@ -15,10 +15,6 @@ body {
border: none;
}
div.arco-divider-horizontal {
border-color: var(--color-neutral-2);
}
li.arco-dropdown-option {
line-height: 32px;
width: calc(100% - 8px);
@ -36,10 +32,6 @@ body {
border-radius: var(--border-radius-small);
overflow: hidden;
}
.arco-modal-header {
// background: var(--color-fill-3);
// border-bottom: none;
}
.arco-modal-footer {
padding-top: 0;
border-top: none;