提交 b6cea4a9 编写于 作者: V Vben

feat(icon-picker): add icon-picker component

上级 b476e1c8
......@@ -7,6 +7,9 @@
### ✨ Features
- axios 支持 form-data 格式请求
- 新增图标选择器组件
- 新增修改密码界面
- 新增部门管理示例界面
### ⚡ Performance Improvements
......
import path from 'path';
import fs from 'fs-extra';
import inquirer from 'inquirer';
import chalk from 'chalk';
import pkg from '../../package.json';
async function generateIcon() {
const dir = path.resolve(process.cwd(), 'node_modules/@iconify/json');
const raw = await fs.readJSON(path.join(dir, 'collections.json'));
const collections = Object.entries(raw).map(([id, v]) => ({
...(v as any),
id,
}));
const choices = collections.map((item) => ({ key: item.id, value: item.id, name: item.name }));
inquirer
.prompt([
{
type: 'checkbox',
name: 'iconSet',
choices: choices,
message: 'Select the icon set that needs to be generated?',
default: true,
},
{
type: 'input',
name: 'output',
message: 'Select the icon set that needs to be generated?',
default: 'src/components/Icon/json',
},
])
.then(async (answers) => {
const { iconSet, output } = answers;
const outputDir = path.resolve(process.cwd(), output);
fs.ensureDir(outputDir);
const genCollections = collections.filter((item) => iconSet.includes(item.id));
const prefixSet: string[] = [];
for (const info of genCollections) {
const data = await fs.readJSON(path.join(dir, 'json', `${info.id}.json`));
if (data) {
const { prefix } = data;
const icons = Object.keys(data.icons).map((item) => `${prefix}:${item}`);
await fs.writeJSON(path.join(output, `${prefix}-info.json`), icons);
prefixSet.push(prefix);
}
}
console.log(
`✨ ${chalk.cyan(`[${pkg.name}]`)}` + ' - Icon generated successfully:' + `[${prefixSet}]`
);
});
}
generateIcon();
......@@ -22,7 +22,9 @@
"test:gzip": "http-server dist --cors --gzip -c-1",
"test:br": "http-server dist --cors --brotli -c-1",
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
"postinstall": "is-ci || husky install"
"install:husky": "is-ci || husky install",
"gen:icon": "esno ./build/gen/generateIconJson.ts",
"postinstall": "npm run install:husky"
},
"dependencies": {
"@iconify/iconify": "^2.0.0-rc.6",
......@@ -55,8 +57,9 @@
"@ls-lint/ls-lint": "^1.9.2",
"@purge-icons/generated": "^0.7.0",
"@types/crypto-js": "^4.0.1",
"@types/fs-extra": "^9.0.7",
"@types/fs-extra": "^9.0.8",
"@types/http-proxy": "^1.17.5",
"@types/inquirer": "^7.3.1",
"@types/lodash-es": "^4.17.4",
"@types/mockjs": "^1.0.3",
"@types/nprogress": "^0.2.0",
......@@ -84,6 +87,7 @@
"fs-extra": "^9.1.0",
"http-server": "^0.12.3",
"husky": "^5.1.2",
"inquirer": "^8.0.0",
"is-ci": "^3.0.0",
"less": "^4.1.1",
"lint-staged": "^10.5.4",
......
import Icon from './src/index.vue';
import IconPicker from './src/IconPicker.vue';
export { Icon, IconPicker };
export { Icon };
export default Icon;
此差异已折叠。
<template>
<a-input
disabled
:style="{ width }"
:placeholder="t('component.icon.placeholder')"
v-model:value="currentSelect"
:class="prefixCls"
>
<template #addonAfter>
<Popover
placement="bottomLeft"
trigger="click"
v-model="visible"
:overlayClassName="`${prefixCls}-popover`"
>
<template #title>
<div class="flex justify-between">
<a-input
:placeholder="t('component.icon.search')"
@change="handleSearchChange"
allowClear
/>
</div>
</template>
<template #content>
<div v-if="getPaginationList.length">
<ScrollContainer class="border border-solid border-t-0">
<ul class="flex flex-wrap px-2">
<li
v-for="icon in getPaginationList"
:key="icon"
:class="currentSelect === icon ? 'bg-primary text-white' : ''"
class="p-2 w-1/8 cursor-pointer mr-1 mt-1 flex justify-center items-center border border-solid hover:bg-primary hover:text-white"
@click="handleClick(icon)"
>
<Icon :icon="icon" />
</li>
</ul>
</ScrollContainer>
<div class="flex py-2 items-center justify-center">
<Pagination
showLessItems
size="small"
:pageSize="pageSize"
:total="getTotal"
@change="handlePageChange"
/>
</div>
</div>
<template v-else
><div class="p-5"> <Empty /></div>
</template>
</template>
<Icon :icon="currentSelect || 'ion:apps-outline'" class="cursor-pointer px-2 py-1" />
</Popover>
</template>
</a-input>
</template>
<script lang="ts">
import { defineComponent, ref, watchEffect, watch, unref } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { ScrollContainer } from '/@/components/Container';
import { Input, Popover, Pagination, Empty } from 'ant-design-vue';
import Icon from './index.vue';
import icons from '../json/ion-info.json';
import { propTypes } from '/@/utils/propTypes';
import { usePagination } from '/@/hooks/web/usePagination';
import { useDebounce } from '/@/hooks/core/useDebounce';
import { useI18n } from '/@/hooks/web/useI18n';
import { useCopyToClipboard } from '/@/hooks/web/useCopyToClipboard';
import { useMessage } from '/@/hooks/web/useMessage';
export default defineComponent({
name: 'IconPicker',
components: { [Input.name]: Input, Icon, Popover, ScrollContainer, Pagination, Empty },
props: {
value: propTypes.string,
width: propTypes.string.def('100%'),
pageSize: propTypes.number.def(140),
copy: propTypes.bool.def(false),
},
emits: ['change'],
setup(props, { emit }) {
const currentSelect = ref('');
const visible = ref(false);
const currentList = ref(icons);
const { prefixCls } = useDesign('icon-picker');
const { t } = useI18n();
const [debounceHandleSearchChange] = useDebounce(handleSearchChange, 100);
const { clipboardRef, isSuccessRef } = useCopyToClipboard(props.value);
const { createMessage } = useMessage();
const { getPaginationList, getTotal, setCurrentPage } = usePagination(
currentList,
props.pageSize
);
watchEffect(() => {
currentSelect.value = props.value;
});
watch(
() => currentSelect.value,
(v) => emit('change', v)
);
function handlePageChange(page: number) {
setCurrentPage(page);
}
function handleClick(icon: string) {
currentSelect.value = icon;
if (props.copy) {
clipboardRef.value = icon;
if (unref(isSuccessRef)) {
createMessage.success(t('component.icon.copy'));
}
}
}
function handleSearchChange(e: ChangeEvent) {
const value = e.target.value;
if (!value) {
setCurrentPage(1);
currentList.value = icons;
return;
}
currentList.value = icons.filter((item) => item.includes(value));
}
return {
t,
prefixCls,
visible,
getTotal,
getPaginationList,
handlePageChange,
handleClick,
currentSelect,
handleSearchChange: debounceHandleSearchChange,
};
},
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-icon-picker';
.@{prefix-cls} {
.ant-input-group-addon {
padding: 0;
}
&-popover {
width: 300px;
.ant-popover-inner-content {
padding: 0;
}
.scrollbar {
height: 220px;
}
}
}
</style>
......@@ -16,6 +16,7 @@
import Iconify from '@purge-icons/generated';
import { isString } from '/@/utils/is';
import { propTypes } from '/@/utils/propTypes';
export default defineComponent({
name: 'GIcon',
props: {
......
......@@ -51,7 +51,7 @@
}
&--dot {
top: calc(50% - 4px);
top: calc(50% - 2px);
width: 6px;
height: 6px;
padding: 0;
......
......@@ -14,7 +14,7 @@
}
span.anticon:not(.app-iconify) {
vertical-align: 0.125em;
vertical-align: 0.135em;
}
.ant-back-top {
......
import type { Ref } from 'vue';
import { ref, unref, computed } from 'vue';
function pagination<T = any>(list: T[], pageNo: number, pageSize: number): T[] {
const offset = (pageNo - 1) * Number(pageSize);
const ret =
offset + Number(pageSize) >= list.length
? list.slice(offset, list.length)
: list.slice(offset, offset + Number(pageSize));
return ret;
}
export function usePagination<T = any>(list: Ref<T[]>, pageSize: number) {
const currentPage = ref(1);
const pageSizeRef = ref(pageSize);
const getPaginationList = computed(() => {
return pagination(unref(list), unref(currentPage), unref(pageSizeRef));
});
const getTotal = computed(() => {
return unref(list).length;
});
function setCurrentPage(page: number) {
currentPage.value = page;
}
function setPageSize(pageSize: number) {
pageSizeRef.value = pageSize;
}
return { setCurrentPage, getTotal, setPageSize, getPaginationList };
}
export default {
placeholder: 'Click the select icon',
search: 'Search icon',
copy: 'Copy icon successfully!',
};
export default {
placeholder: '点击选择图标',
search: '搜索图标',
copy: '复制图标成功!',
};
......@@ -6,9 +6,7 @@ const menu: MenuModule = {
menu: {
name: t('routes.demo.comp.comp'),
path: '/comp',
tag: {
dot: true,
},
children: [
{
path: 'basic',
......@@ -52,9 +50,7 @@ const menu: MenuModule = {
{
path: 'table',
name: t('routes.demo.table.table'),
tag: {
dot: true,
},
children: [
{
path: 'basic',
......@@ -111,16 +107,10 @@ const menu: MenuModule = {
{
path: 'editCellTable',
name: t('routes.demo.table.editCellTable'),
tag: {
dot: true,
},
},
{
path: 'editRowTable',
name: t('routes.demo.table.editRowTable'),
tag: {
dot: true,
},
},
],
},
......
......@@ -14,6 +14,9 @@ const menu: MenuModule = {
{
path: 'icon',
name: t('routes.demo.feat.icon'),
tag: {
content: 'new',
},
},
{
path: 'tabs',
......@@ -51,9 +54,6 @@ const menu: MenuModule = {
{
path: 'ripple',
name: t('routes.demo.feat.ripple'),
tag: {
content: 'new',
},
},
{
path: 'full-screen',
......@@ -89,9 +89,7 @@ const menu: MenuModule = {
{
name: t('routes.demo.feat.breadcrumb'),
path: 'breadcrumb',
tag: {
content: 'new',
},
children: [
{
path: 'flat',
......
......@@ -6,20 +6,33 @@ const menu: MenuModule = {
menu: {
name: t('routes.demo.system.moduleName'),
path: '/system',
tag: {
dot: true,
},
children: [
{
path: 'account',
name: t('routes.demo.system.account'),
tag: {
dot: true,
type: 'warn',
},
},
{
path: 'dept',
name: t('routes.demo.system.dept'),
tag: {
content: 'new',
},
},
{
path: 'changePassword',
name: t('routes.demo.system.password'),
tag: {
content: 'new',
},
},
],
},
......
......@@ -19,7 +19,7 @@ const setting: ProjectConfig = {
settingButtonPosition: SettingButtonPositionEnum.AUTO,
// Permission mode
permissionMode: PermissionModeEnum.BACK,
permissionMode: PermissionModeEnum.ROLE,
// Permission-related cache is stored in sessionStorage or localStorage
permissionCacheType: CacheTypeEnum.SESSION,
......
......@@ -14,17 +14,23 @@
<CollapseContainer title="IconIfy 组件使用" class="my-5">
<div class="flex justify-around flex-wrap">
<Icon icon="fa-solid:address-book" :size="30" />
<Icon icon="mdi-light:bank" :size="30" />
<Icon icon="jam:alien-f" :size="30" />
<Icon icon="jam:android" :size="30" />
<Icon icon="ion:layers-outline" :size="30" />
<Icon icon="ion:bar-chart-outline" :size="30" />
<Icon icon="ion:tv-outline" :size="30" />
<Icon icon="ion:settings-outline" :size="30" />
</div>
</CollapseContainer>
<CollapseContainer title="图标选择器" class="my-5">
<div class="flex justify-around flex-wrap">
<IconPicker />
</div>
</CollapseContainer>
<Alert
show-icon
message="推荐使用Iconify组件"
description="Icon组件基本包含所有的图标,在下面网址内你可以查询到你想要的任何图标。并且打包只会打包所用到的图标。唯一不足的可能就是需要连接外网进行使用。"
description="Icon组件基本包含所有的图标,在下面网址内你可以查询到你想要的任何图标。并且打包只会打包所用到的图标。"
/>
<a-button type="link" @click="toIconify"> Iconify 图标大全 </a-button>
</PageWrapper>
......@@ -43,7 +49,7 @@
CodepenCircleFilled,
} from '@ant-design/icons-vue';
import Icon from '/@/components/Icon/index';
import { Icon, IconPicker } from '/@/components/Icon/index';
import { openWindow } from '/@/utils';
import { PageWrapper } from '/@/components/Page';
......@@ -61,6 +67,7 @@
CodepenCircleFilled,
Icon,
Alert,
IconPicker,
},
setup() {
return {
......
......@@ -2,13 +2,17 @@ import lineClamp from 'windicss/plugin/line-clamp';
import colors from 'windicss/colors';
import { defineConfig } from 'vite-plugin-windicss';
import { primaryColor } from './build/config/themeConfig';
export default defineConfig({
darkMode: 'class',
plugins: [lineClamp, createEnterPlugin()],
theme: {
extend: {
colors,
colors: {
...colors,
primary: primaryColor,
},
screens: {
sm: '576px',
md: '768px',
......
......@@ -1307,10 +1307,10 @@
resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
"@types/fs-extra@^9.0.7":
version "9.0.7"
resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.7.tgz#a9ef2ffdab043def080c5bec94c03402f793577f"
integrity sha512-YGq2A6Yc3bldrLUlm17VNWOnUbnEzJ9CMgOeLFtQF3HOCN5lQBO8VyjG00a5acA5NNSM30kHVGp1trZgnVgi1Q==
"@types/fs-extra@^9.0.8":
version "9.0.8"
resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.8.tgz#32c3c07ddf8caa5020f84b5f65a48470519f78ba"
integrity sha512-bnlTVTwq03Na7DpWxFJ1dvnORob+Otb8xHyUqUWhqvz/Ksg8+JXPlR52oeMSZ37YEOa5PyccbgUNutiQdi13TA==
dependencies:
"@types/node" "*"
......@@ -1379,6 +1379,14 @@
dependencies:
"@types/node" "*"
"@types/inquirer@^7.3.1":
version "7.3.1"
resolved "https://registry.npmjs.org/@types/inquirer/-/inquirer-7.3.1.tgz#1f231224e7df11ccfaf4cf9acbcc3b935fea292d"
integrity sha512-osD38QVIfcdgsPCT0V3lD7eH0OFurX71Jft18bZrsVQWVRt6TuxRzlr0GJLrxoHZR2V5ph7/qP8se/dcnI7o0g==
dependencies:
"@types/through" "*"
rxjs "^6.4.0"
"@types/json-schema@^7.0.3":
version "7.0.7"
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
......@@ -1480,6 +1488,13 @@
resolved "https://registry.npmjs.org/@types/svgo/-/svgo-1.3.3.tgz#4684af265b4e1125c738f5aaafb302c723f4efe0"
integrity sha512-eDLVUvvTn+mol3NpP211DTH9JzSS6YKssRIhHNmXk5BiCl+gc4s+xQQjRFTSsGBohmka5qBsHX6qhL4x88Wkvg==
"@types/through@*":
version "0.0.30"
resolved "https://registry.npmjs.org/@types/through/-/through-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
integrity sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==
dependencies:
"@types/node" "*"
"@types/tinycolor2@^1.4.2":
version "1.4.2"
resolved "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf"
......@@ -1802,7 +1817,7 @@ ansi-escapes@^3.2.0:
resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
ansi-escapes@^4.3.0:
ansi-escapes@^4.2.1, ansi-escapes@^4.3.0:
version "4.3.1"
resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==
......@@ -2537,6 +2552,11 @@ cli-width@^2.0.0:
resolved "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48"
integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==
cli-width@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
cliui@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
......@@ -4015,7 +4035,7 @@ figures@^2.0.0:
dependencies:
escape-string-regexp "^1.0.5"
figures@^3.2.0:
figures@^3.0.0, figures@^3.2.0:
version "3.2.0"
resolved "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
......@@ -5000,6 +5020,25 @@ inquirer@6.5.2:
strip-ansi "^5.1.0"
through "^2.3.6"
inquirer@^8.0.0:
version "8.0.0"
resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz#957a46db1abcf0fdd2ab82deb7470e90afc7d0ac"
integrity sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==
dependencies:
ansi-escapes "^4.2.1"
chalk "^4.1.0"
cli-cursor "^3.1.0"
cli-width "^3.0.0"
external-editor "^3.0.3"
figures "^3.0.0"
lodash "^4.17.21"
mute-stream "0.0.8"
run-async "^2.4.0"
rxjs "^6.6.6"
string-width "^4.1.0"
strip-ansi "^6.0.0"
through "^2.3.6"
interpret@^1.0.0:
version "1.4.0"
resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
......@@ -5720,6 +5759,11 @@ lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
lodash@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
log-symbols@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920"
......@@ -6176,6 +6220,11 @@ mute-stream@0.0.7:
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
mute-stream@0.0.8:
version "0.0.8"
resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
nanoid@^3.0.1, nanoid@^3.1.20:
version "3.1.20"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
......@@ -7536,7 +7585,7 @@ rollup@^2.39.0:
optionalDependencies:
fsevents "~2.3.1"
run-async@^2.2.0:
run-async@^2.2.0, run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
......@@ -7553,6 +7602,13 @@ rxjs@^6.4.0, rxjs@^6.6.3:
dependencies:
tslib "^1.9.0"
rxjs@^6.6.6:
version "6.6.6"
resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70"
integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==
dependencies:
tslib "^1.9.0"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册