first commit

This commit is contained in:
ye1023
2026-04-09 15:09:34 +08:00
commit d8e3a63df1
2246 changed files with 352109 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
loginfo = '/sys/loginfo',
visitInfo = '/sys/visitInfo',
}
/**
* 日志统计信息
* @param params
*/
export const getLoginfo = (params) => defHttp.get({ url: Api.loginfo, params }, { isTransformResponse: false });
/**
* 访问量信息
* @param params
*/
export const getVisitInfo = (params) => defHttp.get({ url: Api.visitInfo, params }, { isTransformResponse: false });
@@ -0,0 +1,128 @@
<template>
<a-card :loading="loading" :bordered="false" :body-style="{ padding: '0' }">
<div class="salesCard">
<a-tabs default-active-key="1" size="large" :tab-bar-style="{ marginBottom: '24px', paddingLeft: '16px' }">
<template #rightExtra>
<div class="extra-wrapper">
<div class="extra-item">
<a>今日</a>
<a>本周</a>
<a>本月</a>
<a>本年</a>
</div>
<a-range-picker :style="{ width: '256px' }" />
</div>
</template>
<a-tab-pane loading="true" tab="受理监管" key="1">
<a-row>
<a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
<Bar :chartData="barData" :option="{ title: { text: '', textStyle: { fontWeight: 'lighter' } } }" height="40vh" :seriesColor="seriesColor" />
</a-col>
<a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
<QuickNav :loading="loading" class="enter-y" :bordered="false" :body-style="{ padding: 0 }" />
</a-col>
</a-row>
</a-tab-pane>
<a-tab-pane tab="交互监管" key="2">
<a-row>
<a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
<BarMulti
:seriesColor="interactiveColor"
:chartData="barMultiData"
:option="{ title: { text: '', textStyle: { fontWeight: 'lighter' } } }"
height="40vh"
/>
</a-col>
<a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
<QuickNav :loading="loading" class="enter-y" :bordered="false" :body-style="{ padding: 0 }" />
</a-col>
</a-row>
</a-tab-pane>
<a-tab-pane tab="存储监管" key="3">
<a-row>
<a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24" style="display: flex">
<Gauge :seriesColor="seriesColor" :chartData="{ name: 'C盘', value: 70 }" height="30vh"></Gauge>
<Gauge :seriesColor="seriesColor" :chartData="{ name: 'D盘', value: 50 }" height="30vh"></Gauge>
</a-col>
<a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
<QuickNav :loading="loading" class="enter-y" :bordered="false" :body-style="{ padding: 0 }" />
</a-col>
</a-row>
</a-tab-pane>
</a-tabs>
</div>
</a-card>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import Bar from '/@/components/chart/Bar.vue';
import BarMulti from '/@/components/chart/BarMulti.vue';
import Gauge from '/@/components/chart/Gauge.vue';
import QuickNav from './QuickNav.vue';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
defineProps({
loading: {
type: Boolean,
},
});
const { getThemeColor } = useRootSetting();
const interactiveColor = ref();
const rankList = [];
for (let i = 0; i < 7; i++) {
rankList.push({
name: '白鹭岛 ' + (i + 1) + ' 号店',
total: 1234.56 - i * 100,
});
}
const barData = [];
for (let i = 0; i < 12; i += 1) {
barData.push({
name: `${i + 1}`,
value: Math.floor(Math.random() * 1000) + 200,
});
}
const barMultiData = [];
for (let j = 0; j < 2; j++) {
for (let i = 0; i < 12; i += 1) {
barMultiData.push({
type: j == 0 ? 'jeecg' : 'jeebt',
name: `${i + 1}`,
value: Math.floor(Math.random() * 1000) + 200,
});
}
}
const seriesColor = computed(() => {
interactiveColor.value = [
{ type: 'jeecg', color: getThemeColor.value },
{ type: 'jeebt', color: getRandomColor() },
];
return getThemeColor.value;
});
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
</script>
<style lang="less" scoped>
.extra-wrapper {
line-height: 55px;
padding-right: 24px;
.extra-item {
display: inline-block;
margin-right: 24px;
a {
margin-left: 24px;
}
}
}
</style>
@@ -0,0 +1,109 @@
<template>
<div class="md:flex">
<template v-for="(item, index) in dataList" :key="item.title">
<ChartCard
:loading="loading"
:title="item.title"
:total="getTotal(item.total, index)"
class="md:w-1/4 w-full !md:mt-0 !mt-4"
:class="[index + 1 < 4 && '!md:mr-4']"
>
<template #action>
<a-tooltip title="指标说明">
<Icon :icon="item.icon" :size="20" />
</a-tooltip>
</template>
<div v-if="type === 'chart'">
<Trend term="周同比" :percentage="12" v-if="index === 0" />
<Trend term="日同比" :percentage="11" v-if="index === 0" :type="false" />
<SingleLine v-if="index === 1" :option="option" :chartData="chartData" :seriesColor="seriesColor" height="50px"></SingleLine>
<Bar v-if="index === 2" :option="option" :chartData="chartData" :seriesColor="seriesColor" height="50px"></Bar>
<Progress v-if="index === 3" :percent="78" :show-info="false"></Progress>
</div>
<div v-else>
<SingleLine :seriesColor="seriesColor" v-if="index === 0" :option="option" :chartData="chartData" height="50px"></SingleLine>
<SingleLine :seriesColor="seriesColor" v-if="index === 1" :option="option" :chartData="chartData" height="50px"></SingleLine>
<Bar :seriesColor="seriesColor" v-if="index === 2" :option="option" :chartData="chartData" height="50px"></Bar>
<Progress v-if="index === 3" :percent="78" :show-info="false"></Progress>
</div>
<template #footer v-if="type === 'chart'">
<span v-if="index !== 3"
>{{ item.footer }}<span>{{ item.value }}</span></span
>
<Trend term="周同比" :percentage="12" v-if="index === 3" />
<Trend term="日同比" :percentage="11" v-if="index === 3" :type="false" />
</template>
<template #footer v-else>
<span
>{{ item.footer }}<span>{{ item.value }}</span></span
>
</template>
</ChartCard>
</template>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import { Icon } from '/@/components/Icon';
import { Progress } from 'ant-design-vue';
import ChartCard from '/@/components/chart/ChartCard.vue';
import Trend from '/@/components/chart/Trend.vue';
import Bar from '/@/components/chart/Bar.vue';
import SingleLine from '/@/components/chart/SingleLine.vue';
import { chartCardList, bdcCardList } from '../data';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
const { getThemeColor } = useRootSetting();
const props = defineProps({
loading: {
type: Boolean,
},
type: {
type: String,
default: 'chart',
},
});
const option = ref({ xAxis: { show: false, boundaryGap: false }, yAxis: { show: false, boundaryGap: false, max: 220 } });
const chartData = ref([
{
name: '1月',
value: 50,
},
{
name: '2月',
value: 100,
},
{
name: '3月',
value: 150,
},
{
name: '4月',
value: 40,
},
{
name: '5月',
value: 110,
},
{
name: '6月',
value: 120,
},
]);
const seriesColor = computed(() => {
return getThemeColor.value;
})
const dataList = computed(() => (props.type === 'dbc' ? bdcCardList : chartCardList));
function getTotal(total, index) {
return index === 0 ? `${total}` : index === 3 ? `${total}%` : total;
}
</script>
@@ -0,0 +1,40 @@
<template>
<div class="md:flex">
<template v-for="(item, index) in growCardList" :key="item.title">
<Card
size="small"
:loading="loading"
:title="item.title"
class="md:w-1/4 w-full !md:mt-0 !mt-4"
:class="[index + 1 < 4 && '!md:mr-4']"
:canExpan="false"
>
<template #extra>
<Tag :color="item.color">{{ item.action }}</Tag>
</template>
<div class="py-4 px-4 flex justify-between">
<CountTo prefix="$" :startVal="1" :endVal="item.value" class="text-2xl" />
<Icon :icon="item.icon" :size="40" />
</div>
<div class="p-2 px-4 flex justify-between">
<span>{{ item.title }}</span>
<CountTo prefix="$" :startVal="1" :endVal="item.total" />
</div>
</Card>
</template>
</div>
</template>
<script lang="ts" setup>
import { CountTo } from '/@/components/CountTo/index';
import { Icon } from '/@/components/Icon';
import { Tag, Card } from 'ant-design-vue';
import { growCardList } from '../data';
defineProps({
loading: {
type: Boolean,
},
});
</script>
@@ -0,0 +1,56 @@
<template>
<Card title="快捷导航" v-bind="$attrs">
<template v-for="item in navItems" :key="item">
<CardGrid @click="goPage">
<span class="flex flex-col items-center">
<Icon :icon="item.icon" :color="item.color" size="20" />
<span class="text-md mt-2">{{ item.title }}</span>
</span>
</CardGrid>
</template>
</Card>
</template>
<script lang="ts" setup>
import { Card } from 'ant-design-vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { Icon } from '/@/components/Icon';
const CardGrid = Card.Grid;
const $message = useMessage();
const navItems = [
{
title: '业务受理',
icon: 'ion:home-outline',
color: '#1fdaca',
},
{
title: '业务管理',
icon: 'ion:grid-outline',
color: '#bf0c2c',
},
{
title: '文件管理',
icon: 'ion:layers-outline',
color: '#e18525',
},
{
title: '信息查询',
icon: 'ion:settings-outline',
color: '#3fb27f',
},
{
title: '通知公告',
icon: 'ion:notifications',
color: '#13b0ff',
},
{
title: '我的任务',
icon: 'ion:person-stalker',
color: '#b27315',
}
];
function goPage() {
$message.createMessage.success('根据业务自行处理跳转页面!');
}
</script>
@@ -0,0 +1,87 @@
<template>
<a-card :loading="loading" :bordered="false" :body-style="{ padding: '0' }">
<div class="salesCard">
<a-tabs default-active-key="1" size="large" :tab-bar-style="{ marginBottom: '24px', paddingLeft: '16px' }">
<template #rightExtra>
<div class="extra-wrapper">
<div class="extra-item">
<a>今日</a>
<a>本周</a>
<a>本月</a>
<a>本年</a>
</div>
<a-range-picker :style="{ width: '256px' }" />
</div>
</template>
<a-tab-pane loading="true" tab="销售额" key="1">
<a-row>
<a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
<Bar :chartData="barData" :option="{ title: { text: '', textStyle: { fontWeight: 'lighter' } } }" height="40vh" :seriesColor="seriesColor" />
</a-col>
<a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
<RankList title="门店销售排行榜" :list="rankList" />
</a-col>
</a-row>
</a-tab-pane>
<a-tab-pane tab="销售趋势" key="2">
<a-row>
<a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24">
<Bar :chartData="barData.reverse()" :option="{ title: { text: '', textStyle: { fontWeight: 'lighter' } } }" height="40vh" :seriesColor="seriesColor" />
</a-col>
<a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24">
<RankList title="门店销售排行榜" :list="rankList" />
</a-col>
</a-row>
</a-tab-pane>
</a-tabs>
</div>
</a-card>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import Bar from '/@/components/chart/Bar.vue';
import RankList from '/@/components/chart/RankList.vue';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
defineProps({
loading: {
type: Boolean,
},
});
const { getThemeColor } = useRootSetting();
const rankList = [];
for (let i = 0; i < 7; i++) {
rankList.push({
name: '白鹭岛 ' + (i + 1) + ' 号店',
total: 1234.56 - i * 100,
});
}
const barData = [];
for (let i = 0; i < 12; i += 1) {
barData.push({
name: `${i + 1}`,
value: Math.floor(Math.random() * 1000) + 200,
});
}
const seriesColor = computed(() => {
return getThemeColor.value
})
</script>
<style lang="less" scoped>
.extra-wrapper {
line-height: 55px;
padding-right: 24px;
.extra-item {
display: inline-block;
margin-right: 24px;
a {
margin-left: 24px;
}
}
}
</style>
@@ -0,0 +1,63 @@
<template>
<Card title="成交占比" :loading="loading">
<div ref="chartRef" :style="{ width, height }"></div>
</Card>
</template>
<script lang="ts" setup>
import { Ref, ref, watch } from 'vue';
import { Card } from 'ant-design-vue';
import { useECharts } from '/@/hooks/web/useECharts';
const props = defineProps({
loading: Boolean,
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '300px',
},
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
watch(
() => props.loading,
() => {
if (props.loading) {
return;
}
setOptions({
tooltip: {
trigger: 'item',
},
series: [
{
name: '访问来源',
type: 'pie',
radius: '80%',
center: ['50%', '50%'],
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
data: [
{ value: 500, name: '电子产品' },
{ value: 310, name: '服装' },
{ value: 274, name: '化妆品' },
{ value: 400, name: '家居' },
].sort(function (a, b) {
return a.value - b.value;
}),
roseType: 'radius',
animationType: 'scale',
animationEasing: 'exponentialInOut',
animationDelay: function () {
return Math.random() * 400;
},
},
],
});
},
{ immediate: true }
);
</script>
@@ -0,0 +1,33 @@
<template>
<Card :tab-list="tabListTitle" v-bind="$attrs" :active-tab-key="activeKey" @tabChange="onTabChange">
<p v-if="activeKey === 'tab1'">
<VisitAnalysis />
</p>
<p v-if="activeKey === 'tab2'">
<VisitAnalysisBar />
</p>
</Card>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Card } from 'ant-design-vue';
import VisitAnalysis from './VisitAnalysis.vue';
import VisitAnalysisBar from './VisitAnalysisBar.vue';
const activeKey = ref('tab1');
const tabListTitle = [
{
key: 'tab1',
tab: '流量趋势',
},
{
key: 'tab2',
tab: '访问量',
},
];
function onTabChange(key) {
activeKey.value = key;
}
</script>
@@ -0,0 +1,104 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref, Ref, watchEffect } from 'vue';
import { useECharts } from '/@/hooks/web/useECharts';
import { basicProps } from './props';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
defineProps({
...basicProps,
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
const { getThemeColor } = useRootSetting();
const init = () => {
setOptions({
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
width: 1,
color: getThemeColor.value,
},
},
},
xAxis: {
type: 'category',
boundaryGap: false,
data: [
'6:00',
'7:00',
'8:00',
'9:00',
'10:00',
'11:00',
'12:00',
'13:00',
'14:00',
'15:00',
'16:00',
'17:00',
'18:00',
'19:00',
'20:00',
'21:00',
'22:00',
'23:00',
],
splitLine: {
show: true,
lineStyle: {
width: 1,
type: 'solid',
color: 'rgba(226,226,226,0.5)',
},
},
axisTick: {
show: false,
},
},
yAxis: [
{
type: 'value',
max: 80000,
splitNumber: 4,
axisTick: {
show: false,
},
splitArea: {
show: true,
areaStyle: {
color: ['rgba(255,255,255,0.2)', 'rgba(226,226,226,0.2)'],
},
},
},
],
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
series: [
{
smooth: true,
data: [111, 222, 4000, 18000, 33333, 55555, 66666, 33333, 14000, 36000, 66666, 44444, 22222, 11111, 4000, 2000, 500, 333, 222, 111],
type: 'line',
areaStyle: {},
itemStyle: {
color: '#5ab1ef',
},
},
{
smooth: true,
data: [33, 66, 88, 333, 3333, 5000, 18000, 3000, 1200, 13000, 22000, 11000, 2221, 1201, 390, 198, 60, 30, 22, 11],
type: 'line',
areaStyle: {},
itemStyle: {
color: getThemeColor.value,
},
},
],
});
};
watchEffect(() => {
init();
});
</script>
@@ -0,0 +1,51 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script lang="ts" setup>
import { onMounted, ref, Ref, watchEffect } from 'vue';
import { useECharts } from '/@/hooks/web/useECharts';
import { basicProps } from './props';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
defineProps({
...basicProps,
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
const { getThemeColor } = useRootSetting();
const init = () => {
setOptions({
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
width: 1,
color: getThemeColor.value,
},
},
},
grid: { left: '1%', right: '1%', top: '2 %', bottom: 0, containLabel: true },
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
},
yAxis: {
type: 'value',
max: 8000,
splitNumber: 4,
},
series: [
{
data: [3000, 2000, 3333, 5000, 3200, 4200, 3200, 2100, 3000, 5100, 6000, 3200, 4800],
type: 'bar',
barMaxWidth: 80,
color: getThemeColor.value,
},
],
});
};
watchEffect(() => {
init();
});
</script>
@@ -0,0 +1,94 @@
<template>
<Card title="转化率" :loading="loading">
<div ref="chartRef" :style="{ width, height }"></div>
</Card>
</template>
<script lang="ts" setup>
import { Ref, ref, watch } from 'vue';
import { Card } from 'ant-design-vue';
import { useECharts } from '/@/hooks/web/useECharts';
const props = defineProps({
loading: Boolean,
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '300px',
},
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
watch(
() => props.loading,
() => {
if (props.loading) {
return;
}
setOptions({
legend: {
bottom: 0,
data: ['访问', '购买'],
},
tooltip: {},
radar: {
radius: '60%',
splitNumber: 8,
indicator: [
{
name: '电脑',
},
{
name: '充电器',
},
{
name: '耳机',
},
{
name: '手机',
},
{
name: 'Ipad',
},
{
name: '耳机',
},
],
},
series: [
{
type: 'radar' as 'custom',
symbolSize: 0,
areaStyle: {
shadowBlur: 0,
shadowColor: 'rgba(0,0,0,.2)',
shadowOffsetX: 0,
shadowOffsetY: 10,
opacity: 1,
},
data: [
{
value: [90, 50, 86, 40, 50, 20],
name: '访问',
itemStyle: {
color: '#b6a2de',
},
},
{
value: [70, 75, 70, 76, 20, 85],
name: '购买',
itemStyle: {
color: '#5ab1ef',
},
},
],
},
],
});
},
{ immediate: true }
);
</script>
@@ -0,0 +1,80 @@
<template>
<Card title="访问来源" :loading="loading">
<div ref="chartRef" :style="{ width, height }"></div>
</Card>
</template>
<script lang="ts" setup>
import { Ref, ref, watch } from 'vue';
import { Card } from 'ant-design-vue';
import { useECharts } from '/@/hooks/web/useECharts';
const props = defineProps({
loading: Boolean,
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '300px',
},
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
watch(
() => props.loading,
() => {
if (props.loading) {
return;
}
setOptions({
tooltip: {
trigger: 'item',
},
legend: {
bottom: '1%',
left: 'center',
},
series: [
{
color: ['#5ab1ef', '#b6a2de', '#67e0e3', '#2ec7c9'],
name: '访问来源',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2,
},
label: {
show: false,
position: 'center',
},
emphasis: {
label: {
show: true,
fontSize: '12',
fontWeight: 'bold',
},
},
labelLine: {
show: false,
},
data: [
{ value: 1048, name: '搜索引擎' },
{ value: 735, name: '直接访问' },
{ value: 580, name: '邮件营销' },
{ value: 484, name: '联盟广告' },
],
animationType: 'scale',
animationEasing: 'exponentialInOut',
animationDelay: function () {
return Math.random() * 100;
},
},
],
});
},
{ immediate: true }
);
</script>
@@ -0,0 +1,16 @@
import { PropType } from 'vue';
export interface BasicProps {
width: string;
height: string;
}
export const basicProps = {
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '280px',
},
};
+219
View File
@@ -0,0 +1,219 @@
export interface GrowCardItem {
icon: string;
title: string;
value?: number;
total: number;
color?: string;
action?: string;
footer?: string;
}
export const growCardList: GrowCardItem[] = [
{
title: '访问数',
icon: 'visit-count|svg',
value: 2000,
total: 120000,
color: 'green',
action: '月',
},
{
title: '成交额',
icon: 'total-sales|svg',
value: 20000,
total: 500000,
color: 'blue',
action: '月',
},
{
title: '下载数',
icon: 'download-count|svg',
value: 8000,
total: 120000,
color: 'orange',
action: '周',
},
{
title: '成交数',
icon: 'transaction|svg',
value: 5000,
total: 50000,
color: 'purple',
action: '年',
},
];
export const chartCardList: GrowCardItem[] = [
{
title: '总销售额',
icon: 'visit-count|svg',
total: 126560,
value: 234.56,
footer: '日均销售额',
},
{
title: '订单量',
icon: 'total-sales|svg',
value: 1234,
total: 8846,
color: 'blue',
footer: '日订单量',
},
{
title: '支付笔数',
icon: 'download-count|svg',
value: 60,
total: 6560,
color: 'orange',
footer: '转化率',
},
{
title: '运营活动效果',
icon: 'transaction|svg',
total: 78,
},
];
export const bdcCardList: GrowCardItem[] = [
{
title: '受理量',
icon: 'ant-design:info-circle-outlined',
total: 100,
value: 60,
footer: '今日受理量',
},
{
title: '办结量',
icon: 'ant-design:info-circle-outlined',
value: 54,
total: 87,
color: 'blue',
footer: '今日办结量',
},
{
title: '用户受理量',
icon: 'ant-design:info-circle-outlined',
value: 13,
total: 15,
color: 'orange',
footer: '用户今日受理量',
},
{
title: '用户办结量',
icon: 'ant-design:info-circle-outlined',
total: 9,
value: 7,
footer: '用户今日办结量',
},
];
export const table = {
dataSource: [
{ reBizCode: '1', type: '转移登记', acceptBy: '张三', acceptDate: '2019-01-22', curNode: '任务分派', flowRate: 60 },
{ reBizCode: '2', type: '抵押登记', acceptBy: '李四', acceptDate: '2019-01-23', curNode: '领导审核', flowRate: 30 },
{ reBizCode: '3', type: '转移登记', acceptBy: '王武', acceptDate: '2019-01-25', curNode: '任务处理', flowRate: 20 },
{ reBizCode: '4', type: '转移登记', acceptBy: '赵楼', acceptDate: '2019-11-22', curNode: '部门审核', flowRate: 80 },
{ reBizCode: '5', type: '转移登记', acceptBy: '钱就', acceptDate: '2019-12-12', curNode: '任务分派', flowRate: 90 },
{ reBizCode: '6', type: '转移登记', acceptBy: '孙吧', acceptDate: '2019-03-06', curNode: '任务处理', flowRate: 10 },
{ reBizCode: '7', type: '抵押登记', acceptBy: '周大', acceptDate: '2019-04-13', curNode: '任务分派', flowRate: 100 },
{ reBizCode: '8', type: '抵押登记', acceptBy: '吴二', acceptDate: '2019-05-09', curNode: '任务上报', flowRate: 50 },
{ reBizCode: '9', type: '抵押登记', acceptBy: '郑爽', acceptDate: '2019-07-12', curNode: '任务处理', flowRate: 63 },
{ reBizCode: '20', type: '抵押登记', acceptBy: '林有', acceptDate: '2019-12-12', curNode: '任务打回', flowRate: 59 },
{ reBizCode: '11', type: '转移登记', acceptBy: '码云', acceptDate: '2019-09-10', curNode: '任务签收', flowRate: 87 },
],
columns: [
{
title: '业务号',
align: 'center',
dataIndex: 'reBizCode',
},
{
title: '业务类型',
align: 'center',
dataIndex: 'type',
},
{
title: '受理人',
align: 'center',
dataIndex: 'acceptBy',
},
{
title: '受理时间',
align: 'center',
dataIndex: 'acceptDate',
},
{
title: '当前节点',
align: 'center',
dataIndex: 'curNode',
},
{
title: '办理时长',
align: 'center',
dataIndex: 'flowRate',
},
],
ipagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条';
},
showQuickJumper: true,
showSizeChanger: true,
total: 0,
},
};
export const table1 = {
dataSource: [
{ reBizCode: 'A001', type: '转移登记', acceptBy: '张四', acceptDate: '2019-01-22', curNode: '任务分派', flowRate: 12 },
{ reBizCode: 'A002', type: '抵押登记', acceptBy: '李吧', acceptDate: '2019-01-23', curNode: '任务签收', flowRate: 3 },
{ reBizCode: 'A003', type: '转移登记', acceptBy: '王三', acceptDate: '2019-01-25', curNode: '任务处理', flowRate: 24 },
{ reBizCode: 'A004', type: '转移登记', acceptBy: '赵二', acceptDate: '2019-11-22', curNode: '部门审核', flowRate: 10 },
{ reBizCode: 'A005', type: '转移登记', acceptBy: '钱大', acceptDate: '2019-12-12', curNode: '任务签收', flowRate: 8 },
{ reBizCode: 'A006', type: '转移登记', acceptBy: '孙就', acceptDate: '2019-03-06', curNode: '任务处理', flowRate: 10 },
{ reBizCode: 'A007', type: '抵押登记', acceptBy: '周晕', acceptDate: '2019-04-13', curNode: '部门审核', flowRate: 24 },
{ reBizCode: 'A008', type: '抵押登记', acceptBy: '吴有', acceptDate: '2019-05-09', curNode: '部门审核', flowRate: 30 },
{ reBizCode: 'A009', type: '抵押登记', acceptBy: '郑武', acceptDate: '2019-07-12', curNode: '任务分派', flowRate: 1 },
{ reBizCode: 'A0010', type: '抵押登记', acceptBy: '林爽', acceptDate: '2019-12-12', curNode: '部门审核', flowRate: 16 },
{ reBizCode: 'A0011', type: '转移登记', acceptBy: '码楼', acceptDate: '2019-09-10', curNode: '部门审核', flowRate: 7 },
],
columns: [
{
title: '业务号',
align: 'center',
dataIndex: 'reBizCode',
},
{
title: '受理人',
align: 'center',
dataIndex: 'acceptBy',
},
{
title: '发起时间',
align: 'center',
dataIndex: 'acceptDate',
},
{
title: '当前节点',
align: 'center',
dataIndex: 'curNode',
},
{
title: '超时时间',
align: 'center',
dataIndex: 'flowRate',
},
],
ipagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条';
},
showQuickJumper: true,
showSizeChanger: true,
total: 0,
},
};
@@ -0,0 +1,244 @@
<template>
<div class="p-4">
<ChartGroupCard class="enter-y" :loading="loading" type="bdc" />
<BdcTabCard class="!my-4 enter-y" :loading="loading" />
<a-row>
<a-col :span="24">
<a-card :loading="loading" :class="{ 'anty-list-cust': true }" :bordered="false">
<a-tabs v-model:activeKey="indexBottomTab" size="large" :tab-bar-style="{ marginBottom: '24px', paddingLeft: '16px' }">
<template #rightExtra>
<div class="extra-wrapper">
<a-radio-group v-model:value="indexRegisterType" @change="changeRegisterType">
<a-radio-button value="转移登记">转移登记</a-radio-button>
<a-radio-button value="抵押登记">抵押登记</a-radio-button>
<a-radio-button value="">所有</a-radio-button>
</a-radio-group>
</div>
</template>
<a-tab-pane tab="业务流程限时监管" key="1">
<a-table
:dataSource="dataSource"
size="default"
rowKey="reBizCode"
:columns="table.columns"
:pagination="ipagination"
@change="tableChange"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'flowRate'">
<Progress
:strokeColor="getPercentColor(record.flowRate)"
:format="getPercentFormat"
:percent="getFlowRateNumber(record.flowRate)"
style="width: 80px"
/>
</template>
</template>
</a-table>
</a-tab-pane>
<a-tab-pane loading="true" tab="业务节点限时监管" key="2">
<a-table
:dataSource="dataSource1"
size="default"
rowKey="reBizCode"
:columns="table1.columns"
:pagination="ipagination1"
@change="tableChange1"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'flowRate'">
<span style="color: red">{{ record.flowRate }}小时</span>
</template>
</template>
</a-table>
</a-tab-pane>
</a-tabs>
</a-card>
</a-col>
</a-row>
</div>
</template>
<script lang="ts" setup>
import { ref, unref } from 'vue';
import { Progress } from 'ant-design-vue';
import ChartGroupCard from '../components/ChartGroupCard.vue';
import BdcTabCard from '../components/BdcTabCard.vue';
import LineMulti from '/@/components/chart/LineMulti.vue';
import HeadInfo from '/@/components/chart/HeadInfo.vue';
import { table, table1 } from '../data';
const loading = ref(true);
setTimeout(() => {
loading.value = false;
}, 500);
const indexBottomTab = ref('1');
const indexRegisterType = ref('转移登记');
const dataSource = ref([]);
const dataSource1 = ref([]);
const ipagination = ref(table.ipagination);
const ipagination1 = ref(table1.ipagination);
function changeRegisterType(e) {
indexRegisterType.value = e.target.value;
if (unref(indexBottomTab) == '1') {
loadDataSource();
} else {
loadDataSource1();
}
}
function tableChange(pagination) {
ipagination.value.current = pagination.current;
ipagination.value.pageSize = pagination.pageSize;
loadDataSource();
}
function tableChange1(pagination) {
ipagination1.value.current = pagination.current;
ipagination1.value.pageSize = pagination.pageSize;
loadDataSource1();
}
function getFlowRateNumber(value) {
return +value;
}
function getPercentFormat(value) {
if (value == 100) {
return '超时';
} else {
return value + '%';
}
}
function getPercentColor(value) {
let p = +value;
if (p >= 90 && p < 100) {
return 'rgb(244, 240, 89)';
} else if (p >= 100) {
return 'red';
} else {
return 'rgb(16, 142, 233)';
}
}
function loadDataSource() {
dataSource.value = table.dataSource.filter((item) => {
if (!unref(indexRegisterType)) {
return true;
}
return item.type == unref(indexRegisterType);
});
}
function loadDataSource1() {
dataSource1.value = table1.dataSource.filter((item) => {
if (!unref(indexRegisterType)) {
return true;
}
return item.type == unref(indexRegisterType);
});
}
loadDataSource();
loadDataSource1();
</script>
<style lang="less" scoped>
.ant-table-wrapper {
:deep(.ant-table){
td,th {
padding: 10px;
}
}
}
.extra-wrapper {
line-height: 55px;
padding-right: 24px;
.extra-item {
display: inline-block;
margin-right: 24px;
a {
margin-left: 24px;
}
}
}
.item-group {
padding: 20px 0 8px 24px;
font-size: 0;
a {
color: rgba(0, 0, 0, 0.65);
display: inline-block;
font-size: 14px;
margin-bottom: 13px;
width: 25%;
}
}
.item-group {
.more-btn {
margin-bottom: 13px;
text-align: center;
}
}
.list-content-item {
color: rgba(0, 0, 0, 0.45);
display: inline-block;
vertical-align: middle;
font-size: 14px;
margin-left: 40px;
}
@media only screen and (min-width: 1600px) {
.list-content-item {
margin-left: 60px;
}
}
@media only screen and (max-width: 1300px) {
.list-content-item {
margin-left: 20px;
}
.width-hidden4 {
display: none;
}
}
.list-content-item {
span {
line-height: 20px;
}
}
.list-content-item {
p {
margin-top: 4px;
margin-bottom: 0;
line-height: 22px;
}
}
.anty-list-cust {
.ant-list-item-meta {
flex: 0.3 !important;
}
}
.anty-list-cust {
.ant-list-item-content {
flex: 1 !important;
justify-content: flex-start !important;
margin-left: 20px;
}
}
</style>
@@ -0,0 +1,149 @@
<template>
<div class="p-4">
<ChartGroupCard class="enter-y" :loading="loading" type="chart" />
<SaleTabCard class="!my-4 enter-y" :loading="loading" />
<a-row>
<a-col :span="24">
<a-card :loading="loading" :bordered="false" title="最近一周访问量统计">
<div class="infoArea">
<HeadInfo title="今日IP" :iconColor="ipColor" :content="loginfo.todayIp" icon="environment"></HeadInfo>
<HeadInfo title="今日访问" :iconColor="visitColor" :content="loginfo.todayVisitCount" icon="team"></HeadInfo>
<HeadInfo title="总访问量" :iconColor="seriesColor" :content="loginfo.totalVisitCount" icon="rise"></HeadInfo>
</div>
<LineMulti :chartData="lineMultiData" height="33vh" type="line" :option="{ legend: { top: 'bottom' } }"></LineMulti>
</a-card>
</a-col>
</a-row>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import ChartGroupCard from '../components/ChartGroupCard.vue';
import SaleTabCard from '../components/SaleTabCard.vue';
import LineMulti from '/@/components/chart/LineMulti.vue';
import HeadInfo from '/@/components/chart/HeadInfo.vue';
import { getLoginfo, getVisitInfo } from '../api.ts';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
const loading = ref(true);
const { getThemeColor } = useRootSetting();
setTimeout(() => {
loading.value = false;
}, 500);
const loginfo = ref({});
const lineMultiData = ref([]);
function initLogInfo() {
getLoginfo(null).then((res) => {
if (res.success) {
Object.keys(res.result).forEach((key) => {
res.result[key] = res.result[key] + '';
});
loginfo.value = res.result;
}
});
getVisitInfo(null).then((res) => {
if (res.success) {
lineMultiData.value = [];
res.result.forEach((item) => {
lineMultiData.value.push({ name: item.type, type: 'ip', value: item.ip, color: ipColor.value });
lineMultiData.value.push({ name: item.type, type: 'visit', value: item.visit, color: visitColor.value });
});
}
});
}
const ipColor = ref();
const visitColor = ref();
const seriesColor = ref();
watch(
() => getThemeColor.value,
() => {
seriesColor.value = getThemeColor.value;
visitColor.value = '#67B962';
ipColor.value = getThemeColor.value;
initLogInfo();
},
{ immediate: true }
);
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
</script>
<style lang="less" scoped>
.infoArea {
display: flex;
justify-content: space-between;
padding: 0 10%;
.head-info.center {
padding: 0;
}
.head-info {
min-width: 0;
}
}
.circle-cust {
position: relative;
top: 28px;
left: -100%;
}
.extra-wrapper {
line-height: 55px;
padding-right: 24px;
.extra-item {
display: inline-block;
margin-right: 24px;
a {
margin-left: 24px;
}
}
}
/* 首页访问量统计 */
.head-info {
position: relative;
text-align: left;
padding: 0 32px 0 0;
min-width: 125px;
&.center {
text-align: center;
padding: 0 32px;
}
span {
color: rgba(0, 0, 0, 0.45);
display: inline-block;
font-size: 0.95rem;
line-height: 42px;
margin-bottom: 4px;
}
p {
line-height: 42px;
margin: 0;
a {
font-weight: 600;
font-size: 1rem;
}
}
}
.ant-card {
::v-deep(.ant-card-head-title) {
font-weight: normal;
}
}
</style>
@@ -0,0 +1,25 @@
<template>
<div class="p-4">
<GrowCard :loading="loading" class="enter-y" />
<SiteAnalysis class="!my-4 enter-y" :loading="loading" />
<div class="md:flex enter-y">
<VisitRadar class="md:w-1/3 w-full" :loading="loading" />
<VisitSource class="md:w-1/3 !md:mx-4 !md:my-0 !my-4 w-full" :loading="loading" />
<SalesProductPie class="md:w-1/3 w-full" :loading="loading" />
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import GrowCard from '../components/GrowCard.vue';
import SiteAnalysis from '../components/SiteAnalysis.vue';
import VisitSource from '../components/VisitSource.vue';
import VisitRadar from '../components/VisitRadar.vue';
import SalesProductPie from '../components/SalesProductPie.vue';
const loading = ref(true);
setTimeout(() => {
loading.value = false;
}, 500);
</script>
@@ -0,0 +1,422 @@
<template>
<div class="index-container-ty">
<a-row type="flex" justify="start" :gutter="3">
<a-col :sm="24" :lg="12">
<a-card>
<template #title>
<div class="index-md-title">
<img src="../../../../assets/images/daiban.png" />
我的待办{{ dataSource1.length }}
</div>
</template>
<template v-if="dataSource1 && dataSource1.length > 0" #extra>
<a @click="goPage"
>更多
<Icon icon="ant-design:double-right-outlined" />
</a>
</template>
<a-table
:class="'my-index-table tytable1'"
ref="table1"
size="small"
rowKey="id"
:columns="columns"
:dataSource="dataSource1"
:pagination="false"
>
<template #ellipsisText="{ text }">
<JEllipsis :value="text" :length="textMaxLength"></JEllipsis>
</template>
<template #dayWarnning="{ text, record }">
<BellTwoTone style="font-size: 22px" :twoToneColor="getTipColor(record)" />
</template>
<template #action="{ text, record }">
<a @click="handleData">办理</a>
</template>
</a-table>
</a-card>
</a-col>
<a-col :sm="24" :lg="12">
<a-card>
<template #title>
<div class="index-md-title">
<img src="../../../../assets/images/zaiban.png" />
我的在办{{ dataSource2.length }}
</div>
</template>
<template v-if="dataSource2 && dataSource2.length > 0" #extra>
<a @click="goPage"
>更多
<Icon icon="ant-design:double-right-outlined" />
</a>
</template>
<a-table
:class="'my-index-table tytable2'"
ref="table1"
size="small"
rowKey="id"
:columns="columns"
:dataSource="dataSource2"
:pagination="false"
>
<template #ellipsisText="{ text }">
<JEllipsis :value="text" :length="textMaxLength"></JEllipsis>
</template>
<template #dayWarnning="{ text, record }">
<BellTwoTone style="font-size: 22px" :twoToneColor="getTipColor(record)" />
</template>
<template #action="{ text, record }">
<a @click="handleData">办理</a>
</template>
</a-table>
</a-card>
</a-col>
<a-col :span="24">
<div style="height: 5px"></div>
</a-col>
<a-col :sm="24" :lg="12">
<a-card>
<template #title>
<div class="index-md-title">
<img src="../../../../assets/images/guaz.png" />
我的挂账{{ dataSource4.length }}
</div>
</template>
<a-table
:class="'my-index-table tytable4'"
ref="table1"
size="small"
rowKey="id"
:columns="columns"
:dataSource="dataSource4"
:pagination="false"
>
<template #ellipsisText="{ text }">
<JEllipsis :value="text" :length="textMaxLength"></JEllipsis>
</template>
<template #dayWarnning="{ text, record }">
<BellTwoTone style="font-size: 22px" :twoToneColor="getTipColor(record)" />
</template>
<template #action="{ text, record }">
<a @click="handleData">办理</a>
</template>
</a-table>
</a-card>
</a-col>
<a-col :sm="24" :lg="12">
<a-card>
<template #title>
<div class="index-md-title">
<img src="../../../../assets/images/duban.png" />
我的督办{{ dataSource3.length }}
</div>
</template>
<a-table
:class="'my-index-table tytable3'"
ref="table1"
size="small"
rowKey="id"
:columns="columns"
:dataSource="dataSource3"
:pagination="false"
>
<template #ellipsisText="{ text }">
<JEllipsis :value="text" :length="textMaxLength"></JEllipsis>
</template>
<template #dayWarnning="{ text, record }">
<BellTwoTone style="font-size: 22px" :twoToneColor="getTipColor(record)" />
</template>
<template #action="{ text, record }">
<a @click="handleData">办理</a>
</template>
</a-table>
</a-card>
</a-col>
</a-row>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import noDataPng from '/@/assets/images/nodata.png';
import { useMessage } from '/@/hooks/web/useMessage';
import JEllipsis from '/@/components/Form/src/jeecg/components/JEllipsis.vue';
import { BulbTwoTone, BellTwoTone } from '@ant-design/icons-vue';
const tempSs1 = [
{
id: '001',
orderNo: '电[1]1267102',
orderTitle: '药品出问题了',
restDay: 1,
},
{
id: '002',
orderNo: '电[4]5967102',
orderTitle: '吃了xxx医院的药,病情越来越严重',
restDay: 0,
},
{
id: '003',
orderNo: '电[3]5988987',
orderTitle: '今天去超市买鸡蛋,鸡蛋都是坏的',
restDay: 7,
},
{
id: '004',
orderNo: '电[2]5213491',
orderTitle: 'xx宝实体店高价售卖xx',
restDay: 5,
},
{
id: '005',
orderNo: '电[1]1603491',
orderTitle: '以红利相诱,答应退保后扣一年费用',
restDay: 0,
},
];
const tempSs2 = [
{
id: '001',
orderTitle: '我要投诉这个大超市',
orderNo: '电[1]10299456',
restDay: 6,
},
{
id: '002',
orderTitle: 'xxx医院乱开药方,售卖假药',
orderNo: '电[2]20235691',
restDay: 0,
},
{
id: '003',
orderTitle: '我想问问这家店是干啥的',
orderNo: '电[3]495867322',
restDay: 7,
},
{
id: '004',
orderTitle: '我要举报朝阳区奥森公园酒店',
orderNo: '电[2]1193849',
restDay: 3,
},
{
id: '005',
orderTitle: '我今天吃饭吃到一个石头子',
orderNo: '电[4]56782344',
restDay: 9,
},
];
//4-7天
const tip_green = 'rgba(0, 255, 0, 1)';
//1-3天
const tip_yellow = 'rgba(255, 255, 0, 1)';
//超期
const tip_red = 'rgba(255, 0, 0, 1)';
const textMaxLength = 8;
const $message = useMessage();
const dataSource1 = ref([]);
const dataSource2 = ref([]);
const dataSource3 = ref([]);
const dataSource4 = ref([]);
const columns = [
{
title: '',
dataIndex: '',
key: 'rowIndex',
width: 50,
fixed: 'left',
align: 'center',
slots: { customRender: 'dayWarnning' },
},
{
title: '剩余天数',
align: 'center',
dataIndex: 'restDay',
width: 80,
},
{
title: '工单标题',
align: 'center',
dataIndex: 'orderTitle',
slots: { customRender: 'ellipsisText' },
},
{
title: '工单编号',
align: 'center',
dataIndex: 'orderNo',
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
slots: { customRender: 'action' },
},
];
function getTipColor(rd) {
let num = rd.restDay;
if (num <= 0) {
return tip_red;
} else if (num >= 1 && num < 4) {
return tip_yellow;
} else if (num >= 4) {
return tip_green;
}
}
function mock() {
dataSource1.value = tempSs1;
dataSource2.value = tempSs2;
dataSource3.value = tempSs1;
dataSource4.value = tempSs2;
ifNullDataSource(dataSource4, '.tytable4');
}
function ifNullDataSource(ds, tb) {
if (!ds || ds.length == 0) {
var tmp = document.createElement('img');
tmp.src = noDataPng;
tmp.width = 300;
let tbclass = `${tb} .ant-table-placeholder`;
document.querySelector(tbclass).innerHTML = '';
document.querySelector(tbclass).appendChild(tmp);
}
}
function handleData() {
$message.createMessage.success('办理完成');
}
function goPage() {
$message.createMessage.success('请根据具体业务跳转页面');
}
mock();
</script>
<style scoped lang="less">
.my-index-table {
height: 270px;
table {
font-size: 14px !important;
}
}
.index-container-ty {
margin: 12px 12px 0;
:deep(.ant-card-body) {
padding: 10px 12px 0 12px;
}
:deep(.ant-card-head) {
line-height: 24px;
min-height: 24px;
background: #7196fb !important;
.ant-card-head-title {
padding-top: 6px;
padding-bottom: 6px;
}
.ant-card-extra {
padding: 0;
a {
color: #fff;
}
a:hover {
color: #152ede;
}
}
}
:deep(.ant-table-footer) {
text-align: right;
padding: 6px 12px 6px 6px;
background: #fff;
border-top: 2px solid #f7f1f1;
}
.index-md-title {
position: relative;
width: 100%;
color: #fff;
font-size: 21px;
font-family: cursive;
padding-left: 25px;
img {
position: absolute;
height: 25px;
left: 0;
}
}
:deep(.ant-table-thead > tr > th),
:deep(.ant-table-tbody > tr > td) {
border-bottom: 1px solid #90aeff;
}
:deep(
.ant-table-small
> .ant-table-content
> .ant-table-fixed-left
> .ant-table-body-outer
> .ant-table-body-inner
> table
> .ant-table-thead
> tr
> th),
:deep(
.ant-table-small
> .ant-table-content
> .ant-table-fixed-right
> .ant-table-body-outer
> .ant-table-body-inner
> table
> .ant-table-thead
> tr
> th) {
border-bottom: 1px solid #90aeff;
}
:deep(.ant-table-small > .ant-table-content > .ant-table-scroll > .ant-table-body > table > .ant-table-thead > tr > th) {
border-bottom: 1px solid #90aeff;
}
:deep(.ant-table-small) {
border: 1px solid #90aeff;
}
:deep(.ant-table-placeholder) {
padding: 0;
height: 215px;
}
}
</style>
+24
View File
@@ -0,0 +1,24 @@
<template>
<IndexChart v-if="indexStyle === 0"></IndexChart>
<IndexDef v-if="indexStyle === 1"></IndexDef>
<IndexBdc v-if="indexStyle == 2"></IndexBdc>
<IndexTask v-if="indexStyle == 3"></IndexTask>
<div style="width: 100%; text-align: right; margin-top: 20px">
首页主题
<a-radio-group v-model:value="indexStyle">
<a-radio :value="0">默认</a-radio>
<a-radio :value="1">销量统计</a-radio>
<a-radio :value="2">业务统计</a-radio>
<a-radio :value="3">我的任务</a-radio>
</a-radio-group>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import IndexDef from './homePage/IndexDef.vue';
import IndexChart from './homePage/IndexChart.vue';
import IndexBdc from './homePage/IndexBdc.vue';
import IndexTask from './homePage/IndexTask.vue';
const indexStyle = ref(0);
</script>
Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

@@ -0,0 +1,77 @@
<template>
<div v-if="visible" ref="aideWrapRef" class="aide-wrap" @click="handleGo">
<a-popconfirm
:open="popconfirmVisible"
placement="topRight"
title="确定AI助手退出吗?"
ok-text="确定"
cancel-text="取消"
@cancel="handleCancel"
@confirm="handleConfirm"
>
<img :src="aiImage" alt="ai助手" />
</a-popconfirm>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { router } from '/@/router';
import { AIDE_FLAG } from '/@/enums/cacheEnum';
import { getToken } from '/@/utils/auth';
import { getAuthCache, setAuthCache, removeAuthCache } from '/@/utils/auth';
import aiImage from './images/ai.png';
const visible = ref(1);
const aideWrapRef = ref(null);
const popconfirmVisible = ref(false);
onMounted(() => {
const aideFlag = getAuthCache(AIDE_FLAG);
if (aideFlag && aideFlag == getToken()) {
visible.value = false;
} else {
visible.value = true;
}
if (visible.value) {
aideWrapRef.value.addEventListener('contextmenu', (e) => {
popconfirmVisible.value = true;
e.preventDefault();
});
}
});
const handleCancel = () => {
popconfirmVisible.value = false;
};
const handleConfirm = () => {
popconfirmVisible.value = false;
visible.value = false;
setAuthCache(AIDE_FLAG, getToken());
};
const handleGo = (params) => {
router.push({ path: '/ai' });
};
</script>
<style lang="less" scoped>
.aide-wrap {
position: fixed;
top: 50%;
right: 5px;
transform: translate(0, -50%);
height: 33px;
width: 33px;
border-radius: 50%;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
line-height: 1;
color: #fff;
cursor: pointer;
.text {
font-size: 12px;
transform: scale(0.88);
}
}
</style>
+24
View File
@@ -0,0 +1,24 @@
<template>
<div class="wrap">
<div class="content">
<AiChat></AiChat>
</div>
</div>
</template>
<script setup>
import AiChat from '/@/views/super/airag/aiapp/chat/AiChat.vue';
</script>
<style lang="less" scoped>
.wrap {
height: 100%;
width: 100%;
.content {
background: #fff;
width: 100%;
height: 100%;
}
}
</style>
@@ -0,0 +1,31 @@
<template>
<Card title="最新动态" v-bind="$attrs">
<template #extra>
<a-button type="link" size="small">更多</a-button>
</template>
<List item-layout="horizontal" :data-source="dynamicInfoItems">
<template #renderItem="{ item }">
<ListItem>
<ListItemMeta>
<template #description>
{{ item.date }}
</template>
<!-- eslint-disable-next-line -->
<template #title> {{ item.name }} <span v-html="item.desc"> </span> </template>
<template #avatar>
<Icon :icon="item.avatar" :size="30" />
</template>
</ListItemMeta>
</ListItem>
</template>
</List>
</Card>
</template>
<script lang="ts" setup>
import { Card, List } from 'ant-design-vue';
import { dynamicInfoItems } from './data';
import { Icon } from '/@/components/Icon';
const ListItem = List.Item;
const ListItemMeta = List.Item.Meta;
</script>
@@ -0,0 +1,34 @@
<template>
<Card title="项目" v-bind="$attrs">
<template #extra>
<a-button type="link" size="small">更多</a-button>
</template>
<template v-for="item in items" :key="item">
<CardGrid class="!md:w-1/3 !w-full">
<span class="flex">
<Icon :icon="item.icon" :color="item.color" size="30" />
<span class="text-lg ml-4">{{ item.title }}</span>
</span>
<div class="flex mt-2 h-10 text-secondary"> {{ item.desc }} </div>
<div class="flex justify-between text-secondary">
<span>{{ item.group }}</span>
<span>{{ item.date }}</span>
</div>
</CardGrid>
</template>
</Card>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Card } from 'ant-design-vue';
import { Icon } from '/@/components/Icon';
import { groupItems } from './data';
export default defineComponent({
components: { Card, CardGrid: Card.Grid, Icon },
setup() {
return { items: groupItems };
},
});
</script>
@@ -0,0 +1,19 @@
<template>
<Card title="快捷导航" v-bind="$attrs">
<template v-for="item in navItems" :key="item">
<CardGrid>
<span class="flex flex-col items-center">
<Icon :icon="item.icon" :color="item.color" size="20" />
<span class="text-md mt-2">{{ item.title }}</span>
</span>
</CardGrid>
</template>
</Card>
</template>
<script lang="ts" setup>
import { Card } from 'ant-design-vue';
import { navItems } from './data';
import { Icon } from '/@/components/Icon';
const CardGrid = Card.Grid;
</script>
@@ -0,0 +1,94 @@
<template>
<Card title="销售统计" :loading="loading">
<div ref="chartRef" :style="{ width, height }"></div>
</Card>
</template>
<script lang="ts" setup>
import { Ref, ref, watch } from 'vue';
import { Card } from 'ant-design-vue';
import { useECharts } from '/@/hooks/web/useECharts';
const props = defineProps({
loading: Boolean,
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '400px',
},
});
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
watch(
() => props.loading,
() => {
if (props.loading) {
return;
}
setOptions({
legend: {
bottom: 0,
data: ['Visits', 'Sales'],
},
tooltip: {},
radar: {
radius: '60%',
splitNumber: 8,
indicator: [
{
name: '2017',
},
{
name: '2017',
},
{
name: '2018',
},
{
name: '2019',
},
{
name: '2020',
},
{
name: '2021',
},
],
},
series: [
{
type: 'radar' as 'custom',
symbolSize: 0,
areaStyle: {
shadowBlur: 0,
shadowColor: 'rgba(0,0,0,.2)',
shadowOffsetX: 0,
shadowOffsetY: 10,
opacity: 1,
},
data: [
{
value: [90, 50, 86, 40, 50, 20],
name: 'Visits',
itemStyle: {
color: '#b6a2de',
},
},
{
value: [70, 75, 70, 76, 20, 85],
name: 'Sales',
itemStyle: {
color: '#67e0e3',
},
},
],
},
],
});
},
{ immediate: true }
);
</script>
@@ -0,0 +1,33 @@
<template>
<div class="lg:flex">
<Avatar :src="userinfo.avatar || headerImg" :size="72" class="!mx-auto !block" />
<div class="md:ml-6 flex flex-col justify-center md:mt-0 mt-2">
<h1 class="md:text-lg text-md">早安, {{ userinfo.realname }}, 开始您一天的工作吧</h1>
<span class="text-secondary"> 今日晴20 - 32 </span>
</div>
<div class="flex flex-1 justify-end md:mt-0 mt-4">
<div class="flex flex-col justify-center text-right">
<span class="text-secondary"> 待办 </span>
<span class="text-2xl">2/10</span>
</div>
<div class="flex flex-col justify-center text-right md:mx-16 mx-12">
<span class="text-secondary"> 项目 </span>
<span class="text-2xl">8</span>
</div>
<div class="flex flex-col justify-center text-right md:mr-10 mr-4">
<span class="text-secondary"> 团队 </span>
<span class="text-2xl">300</span>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue';
import { Avatar } from 'ant-design-vue';
import { useUserStore } from '/@/store/modules/user';
import headerImg from '/@/assets/images/header.jpg';
const userStore = useUserStore();
const userinfo = computed(() => userStore.getUserInfo);
</script>
@@ -0,0 +1,156 @@
interface GroupItem {
title: string;
icon: string;
color: string;
desc: string;
date: string;
group: string;
}
interface NavItem {
title: string;
icon: string;
color: string;
}
interface DynamicInfoItem {
avatar: string;
name: string;
date: string;
desc: string;
}
export const navItems: NavItem[] = [
{
title: '首页',
icon: 'ion:home-outline',
color: '#1fdaca',
},
{
title: '仪表盘',
icon: 'ion:grid-outline',
color: '#bf0c2c',
},
{
title: '组件',
icon: 'ion:layers-outline',
color: '#e18525',
},
{
title: '系统管理',
icon: 'ion:settings-outline',
color: '#3fb27f',
},
{
title: '权限管理',
icon: 'ion:key-outline',
color: '#4daf1bc9',
},
{
title: '图表',
icon: 'ion:bar-chart-outline',
color: '#00d8ff',
},
];
export const dynamicInfoItems: DynamicInfoItem[] = [
{
avatar: 'dynamic-avatar-1|svg',
name: '威廉',
date: '刚刚',
desc: `在 <a>开源组</a> 创建了项目 <a>Vue</a>`,
},
{
avatar: 'dynamic-avatar-2|svg',
name: '艾文',
date: '1个小时前',
desc: `关注了 <a>威廉</a> `,
},
{
avatar: 'dynamic-avatar-3|svg',
name: '克里斯',
date: '1天前',
desc: `发布了 <a>个人动态</a> `,
},
{
avatar: 'dynamic-avatar-4|svg',
name: 'Jeecg',
date: '2天前',
desc: `发表文章 <a>如何编写一个Vite插件</a> `,
},
{
avatar: 'dynamic-avatar-5|svg',
name: '皮特',
date: '3天前',
desc: `回复了 <a>杰克</a> 的问题 <a>如何进行项目优化?</a>`,
},
{
avatar: 'dynamic-avatar-6|svg',
name: '杰克',
date: '1周前',
desc: `关闭了问题 <a>如何运行项目</a> `,
},
{
avatar: 'dynamic-avatar-1|svg',
name: '威廉',
date: '1周前',
desc: `发布了 <a>个人动态</a> `,
},
{
avatar: 'dynamic-avatar-1|svg',
name: '威廉',
date: '2021-04-01 20:00',
desc: `推送了代码到 <a>Github</a>`,
},
];
export const groupItems: GroupItem[] = [
{
title: 'Github',
icon: 'carbon:logo-github',
color: '',
desc: '不要等待机会,而要创造机会。',
group: '开源组',
date: '2021-04-01',
},
{
title: 'Vue',
icon: 'ion:logo-vue',
color: '#3fb27f',
desc: '现在的你决定将来的你。',
group: '算法组',
date: '2021-04-01',
},
{
title: 'Html5',
icon: 'ion:logo-html5',
color: '#e18525',
desc: '没有什么才能比努力更重要。',
group: '上班摸鱼',
date: '2021-04-01',
},
{
title: 'Angular',
icon: 'ion:logo-angular',
color: '#bf0c2c',
desc: '热情和欲望可以突破一切难关。',
group: 'UI',
date: '2021-04-01',
},
{
title: 'React',
icon: 'bx:bxl-react',
color: '#00d8ff',
desc: '健康的身体是实目标的基石。',
group: '技术牛',
date: '2021-04-01',
},
{
title: 'Js',
icon: 'ion:logo-javascript',
color: '#4daf1bc9',
desc: '路是走出来的,而不是空想出来的。',
group: '架构组',
date: '2021-04-01',
},
];
+36
View File
@@ -0,0 +1,36 @@
<template>
<PageWrapper>
<template #headerContent> <WorkbenchHeader /> </template>
<div class="lg:flex">
<div class="lg:w-7/10 w-full !mr-4 enter-y">
<ProjectCard :loading="loading" class="enter-y" />
<DynamicInfo :loading="loading" class="!my-4 enter-y" />
</div>
<div class="lg:w-3/10 w-full enter-y">
<QuickNav :loading="loading" class="enter-y" />
<Card class="!my-4 enter-y" :loading="loading">
<img class="xl:h-50 h-30 mx-auto" src="../../../assets/svg/illustration.svg" />
</Card>
<SaleRadar :loading="loading" class="enter-y" />
</div>
</div>
</PageWrapper>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { Card } from 'ant-design-vue';
import { PageWrapper } from '/@/components/Page';
import WorkbenchHeader from './components/WorkbenchHeader.vue';
import ProjectCard from './components/ProjectCard.vue';
import QuickNav from './components/QuickNav.vue';
import DynamicInfo from './components/DynamicInfo.vue';
import SaleRadar from './components/SaleRadar.vue';
const loading = ref(true);
setTimeout(() => {
loading.value = false;
}, 500);
</script>
@@ -0,0 +1,12 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
genArticle = '/test/ai/gen/article',
}
/**
* @param param
*/
export const genArticle = (params) => {
return defHttp.post({ url: Api.genArticle + '?prompt=' + params.descr, params, timeout: 300000 });
};
+44
View File
@@ -0,0 +1,44 @@
<template>
<PageWrapper title="AIGC-生成软文" contentBackground>
<a-row>
<a-col :span="12"><a-input v-model:value="descr" placeholder="输入软文的关键字" /></a-col>
<a-col :span="12"><a-button type="primary" @click="handleSubmit">确定</a-button></a-col>
</a-row>
<div class="mt-2">
<a-card title="生成结果">
<MarkdownViewer :value="resultData" />
</a-card>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { genArticle } from './aiGenArticle.api';
import { MarkdownViewer } from '/@/components/Markdown';
export default defineComponent({
components: { PageWrapper, MarkdownViewer },
setup() {
const descr = ref('JeecgBoot的介绍和优势的文章');
const resultData = ref('');
function handleSubmit() {
genArticle({ descr: descr.value }).then((resp) => {
resultData.value = resp;
console.log(resultData);
});
}
return {
descr,
resultData,
handleSubmit,
};
},
});
</script>
<style lang="less" scoped>
.desc-wrap {
padding: 16px;
background-color: @component-background;
}
</style>
+12
View File
@@ -0,0 +1,12 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
genCode = '/test/ai/gen/schema/modules',
}
/**
* @param param
*/
export const genCode = (params) => {
return defHttp.post({ url: Api.genCode + '?prompt=' + params.descr, params, timeout: 300000 });
};
+44
View File
@@ -0,0 +1,44 @@
<template>
<PageWrapper title="AIGC-生成表结构" contentBackground>
<a-row>
<a-col :span="12"><a-input v-model:value="descr" placeholder="输入业务描述" /></a-col>
<a-col :span="12"><a-button type="primary" @click="handleSubmit">确定</a-button></a-col>
</a-row>
<div class="mt-2">
<a-card title="生成结果">
<MarkdownViewer :value="resultData" />
</a-card>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { genCode } from './aiGenCode.api';
import { MarkdownViewer } from '/@/components/Markdown';
export default defineComponent({
components: { PageWrapper, MarkdownViewer },
setup() {
const descr = ref('电商系统相关的表:包含:会员 店铺 商品 商品规格 购物车 订单 订单物流');
const resultData = ref('');
function handleSubmit() {
genCode({ descr: descr.value }).then((resp) => {
resultData.value = '```\n' + resp + '\n```';
console.log(resultData);
});
}
return {
descr,
resultData,
handleSubmit,
};
},
});
</script>
<style lang="less" scoped>
.desc-wrap {
padding: 16px;
background-color: @component-background;
}
</style>
+117
View File
@@ -0,0 +1,117 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, Ref, onMounted } from 'vue';
import { useECharts } from '/@/hooks/web/useECharts';
import { getLineData } from './data';
export default defineComponent({
props: {
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: 'calc(100vh - 78px)',
},
},
setup() {
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
const { barData, lineData, category } = getLineData;
onMounted(() => {
setOptions({
backgroundColor: '#0f375f',
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
label: {
show: true,
backgroundColor: '#333',
},
},
},
legend: {
data: ['line', 'bar'],
textStyle: {
color: '#ccc',
},
},
xAxis: {
data: category,
axisLine: {
lineStyle: {
color: '#ccc',
},
},
},
yAxis: {
splitLine: { show: false },
axisLine: {
lineStyle: {
color: '#ccc',
},
},
},
series: [
{
name: 'line',
type: 'line',
smooth: true,
showAllSymbol: 'auto',
symbol: 'emptyCircle',
symbolSize: 15,
data: lineData,
},
{
name: 'bar',
type: 'bar',
barWidth: 10,
itemStyle: {
borderRadius: 5,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#14c8d4' },
{ offset: 1, color: '#43eec6' },
]),
},
data: barData,
},
{
name: 'line',
type: 'bar',
barGap: '-100%',
barWidth: 10,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(20,200,212,0.5)' },
{ offset: 0.2, color: 'rgba(20,200,212,0.2)' },
{ offset: 1, color: 'rgba(20,200,212,0)' },
]),
},
z: -12,
data: lineData,
},
{
name: 'dotted',
type: 'pictorialBar',
symbol: 'rect',
itemStyle: {
color: '#0f375f',
},
symbolRepeat: true,
symbolSize: [12, 4],
symbolMargin: 1,
z: -10,
data: lineData,
},
],
});
});
return { chartRef };
},
});
</script>
+75
View File
@@ -0,0 +1,75 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, Ref, onMounted } from 'vue';
import { useECharts } from '/@/hooks/web/useECharts';
import { mapData } from './data';
import { registerMap } from 'echarts';
export default defineComponent({
props: {
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: 'calc(100vh - 78px)',
},
},
setup() {
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
onMounted(async () => {
const json = (await (await import('./china.json')).default) as any;
registerMap('china', json);
setOptions({
visualMap: [
{
min: 0,
max: 1000,
left: 'left',
top: 'bottom',
text: ['高', '低'],
calculable: false,
orient: 'horizontal',
inRange: {
color: ['#e0ffff', '#006edd'],
symbolSize: [30, 100],
},
},
],
tooltip: {
trigger: 'item',
backgroundColor: 'rgba(0, 0, 0, .6)',
textStyle: {
color: '#fff',
fontSize: 12,
},
},
series: [
{
name: 'iphone4',
type: 'map',
map: 'china',
label: {
show: true,
color: 'rgb(249, 249, 249)',
fontSize: 10,
},
itemStyle: {
areaColor: '#2f82ce',
borderColor: '#0DAAC1',
},
data: mapData,
},
],
});
});
return { chartRef };
},
});
</script>
+135
View File
@@ -0,0 +1,135 @@
<template>
<div ref="chartRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, Ref, onMounted } from 'vue';
import { useECharts } from '/@/hooks/web/useECharts';
export default defineComponent({
props: {
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: 'calc(100vh - 78px)',
},
},
setup() {
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
const dataAll = [389, 259, 262, 324, 232, 176, 196, 214, 133, 370];
const yAxisData = ['原因1', '原因2', '原因3', '原因4', '原因5', '原因6', '原因7', '原因8', '原因9', '原因10'];
onMounted(() => {
setOptions({
backgroundColor: '#0f375f',
title: [
{
text: '各渠道投诉占比',
left: '2%',
top: '1%',
textStyle: {
color: '#fff',
fontSize: 14,
},
},
{
text: '投诉原因TOP10',
left: '40%',
top: '1%',
textStyle: {
color: '#fff',
fontSize: 14,
},
},
{
text: '各级别投诉占比',
left: '2%',
top: '50%',
textStyle: {
color: '#fff',
fontSize: 14,
},
},
],
grid: [{ left: '50%', top: '7%', width: '45%', height: '90%' }],
tooltip: {
formatter: '{b} ({c})',
},
xAxis: [
{
gridIndex: 0,
axisTick: { show: false },
axisLabel: { show: false },
splitLine: { show: false },
axisLine: { show: false },
},
],
yAxis: [
{
gridIndex: 0,
interval: 0,
data: yAxisData.reverse(),
axisTick: { show: false },
axisLabel: { show: true },
splitLine: { show: false },
axisLine: { show: true, lineStyle: { color: '#6173a3' } },
},
],
series: [
{
name: '各渠道投诉占比',
type: 'pie',
radius: '30%',
center: ['22%', '25%'],
data: [
{ value: 335, name: '客服电话' },
{ value: 310, name: '奥迪官网' },
{ value: 234, name: '媒体曝光' },
{ value: 135, name: '质检总局' },
{ value: 105, name: '其他' },
],
labelLine: { show: false },
label: {
show: true,
formatter: '{b} \n ({d}%)',
color: '#B1B9D3',
},
},
{
name: '各级别投诉占比',
type: 'pie',
radius: '30%',
center: ['22%', '75%'],
labelLine: { show: false },
data: [
{ value: 335, name: 'A级' },
{ value: 310, name: 'B级' },
{ value: 234, name: 'C级' },
{ value: 135, name: 'D级' },
],
label: {
show: true,
formatter: '{b} \n ({d}%)',
color: '#B1B9D3',
},
},
{
name: '投诉原因TOP10',
type: 'bar',
xAxisIndex: 0,
yAxisIndex: 0,
barWidth: '45%',
itemStyle: { color: '#86c9f4' },
label: { show: true, position: 'right', color: '#9EA7C4' },
data: dataAll.sort(),
},
],
});
});
return { chartRef };
},
});
</script>
+100
View File
@@ -0,0 +1,100 @@
<template>
<Card title="销售统计" :loading="loading">
<div ref="chartRef" :style="{ width, height }"></div>
</Card>
</template>
<script lang="ts">
import type { Ref } from 'vue';
import { defineComponent, ref, watch } from 'vue';
import { Card } from 'ant-design-vue';
import { useECharts } from '/@/hooks/web/useECharts';
export default defineComponent({
components: { Card },
props: {
loading: Boolean,
width: {
type: String as PropType<string>,
default: '100%',
},
height: {
type: String as PropType<string>,
default: '400px',
},
},
setup(props) {
const chartRef = ref<HTMLDivElement | null>(null);
const { setOptions } = useECharts(chartRef as Ref<HTMLDivElement>);
watch(
() => props.loading,
() => {
if (props.loading) {
return;
}
setOptions({
legend: {
bottom: 0,
data: ['Visits', 'Sales'],
},
tooltip: {},
radar: {
radius: '60%',
splitNumber: 8,
indicator: [
{
name: '2017',
},
{
name: '2017',
},
{
name: '2018',
},
{
name: '2019',
},
{
name: '2020',
},
{
name: '2021',
},
],
},
series: [
{
type: 'radar' as 'custom',
symbolSize: 0,
areaStyle: {
shadowBlur: 0,
shadowColor: 'rgba(0,0,0,.2)',
shadowOffsetX: 0,
shadowOffsetY: 10,
opacity: 1,
},
data: [
{
value: [90, 50, 86, 40, 50, 20],
name: 'Visits',
itemStyle: {
color: '#9f8ed7',
},
},
{
value: [70, 75, 70, 76, 20, 85],
name: 'Sales',
itemStyle: {
color: '#1edec5',
},
},
],
},
],
});
},
{ immediate: true }
);
return { chartRef };
},
});
</script>
+839
View File
@@ -0,0 +1,839 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "710000",
"properties": {
"id": "710000",
"cp": [121.509062, 24.044332],
"name": "台湾",
"childNum": 6
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@°Ü¯Û"],
["@@ƛĴÕƊÉɼģºðʀ\\ƎsÆNŌÔĚäœnÜƤɊĂǀĆĴžĤNJŨxĚĮǂƺòƌ‚–âÔ®ĮXŦţƸZûЋƕƑGđ¨ĭMó·ęcëƝɉlÝƯֹÅŃ^Ó·śŃNjƏďíåɛGɉ™¿@ăƑŽ¥ĘWǬÏĶŁâ"],
["@@\\p|WoYG¿¥I†j@¢"],
["@@…¡‰@ˆV^RqˆBbAŒnTXeRz¤Lž«³I"],
["@@ÆEE—„kWqë @œ"],
["@@fced"],
["@@„¯ɜÄèaì¯ØǓIġĽ"],
["@@çûĖ롖hòř "]
],
"encodeOffsets": [
[[122886, 24033]],
[[123335, 22980]],
[[122375, 24193]],
[[122518, 24117]],
[[124427, 22618]],
[[124862, 26043]],
[[126259, 26318]],
[[127671, 26683]]
]
}
},
{
"type": "Feature",
"id": "130000",
"properties": {
"id": "130000",
"cp": [114.502461, 38.045474],
"name": "河北",
"childNum": 3
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@o~†Z]‚ªr‰ºc_ħ²G¼s`jΟnüsœłNX_“M`ǽÓnUK…Ĝēs¤­©yrý§uģŒc†JŠ›e"],
["@@U`Ts¿m‚"],
[
"@@oºƋÄd–eVŽDJj£€J|Ådz•Ft~žKŨ¸IÆv|”‡¢r}膎onb˜}`RÎÄn°ÒdÞ²„^®’lnÐèĄlðӜ×]ªÆ}LiĂ±Ö`^°Ç¶p®đDcœŋ`–ZÔ’¶êqvFƚ†N®ĆTH®¦O’¾ŠIbÐã´BĐɢŴÆíȦp–ĐÞXR€·nndOž¤’OÀĈƒ­Qg˜µFo|gȒęSWb©osx|hYh•gŃfmÖĩnº€T̒Sp›¢dYĤ¶UĈjl’ǐpäìë|³kÛfw²Xjz~ÂqbTŠÑ„ěŨ@|oM‡’zv¢ZrÃVw¬ŧˏfŒ°ÐT€ªqŽs{Sž¯r æÝlNd®²Ğ džiGʂJ™¼lr}~K¨ŸƐÌWö€™ÆŠzRš¤lêmĞL΄’@¡|q]SvK€ÑcwpÏρ†ĿćènĪWlĄkT}ˆJ”¤~ƒÈT„d„™pddʾĬŠ”ŽBVt„EÀ¢ôPĎƗè@~‚k–ü\\rÊĔÖæW_§¼F˜†´©òDòj’ˆYÈrbĞāøŀG{ƀ|¦ðrb|ÀH`pʞkv‚GpuARhÞÆǶgƊTǼƹS£¨¡ù³ŘÍ]¿Ây™ôEP xX¶¹܇O¡“gÚ¡IwÃ鑦ÅB‡Ï|ǰ…N«úmH¯‹âŸDùŽyŜžŲIÄuШDž•¸dɂ‡‚FŸƒ•›Oh‡đ©OŸ›iÃ`ww^ƒÌkŸ‘ÑH«ƇǤŗĺtFu…{Z}Ö@U‡´…ʚLg®¯Oı°ÃwŸ ^˜—€VbÉs‡ˆmA…ê]]w„§›RRl£‡ȭµu¯b{ÍDěïÿȧŽuT£ġƒěŗƃĝ“Q¨fV†Ƌ•ƅn­a@‘³@šď„yýIĹÊKšŭfċŰóŒxV@tˆƯŒJ”]eƒR¾fe|rHA˜|h~Ėƍl§ÏŠlTíb ØoˆÅbbx³^zÃ͚¶Sj®A”yÂhðk`š«P€”ˈµEF†Û¬Y¨Ļrõqi¼‰Wi°§’б´°^[ˆÀ|ĠO@ÆxO\\tŽa\\tĕtû{ġŒȧXýĪÓjùÎRb›š^ΛfK[ݏděYfíÙTyŽuUSyŌŏů@Oi½’éŅ­aVcř§ax¹XŻác‡žWU£ôãºQ¨÷Ñws¥qEH‰Ù|‰›šYQoŕÇyáĂ£MðoťÊ‰P¡mšWO¡€v†{ôvîēÜISpÌhp¨ ‘j†deŔQÖj˜X³à™Ĉ[n`Yp@Už–cM`’RKhŒEbœ”pŞlNut®Etq‚nsÁŠgA‹iú‹oH‡qCX‡”hfgu“~ϋWP½¢G^}¯ÅīGCŸÑ^ãziMáļMTÃƘrMc|O_ž¯Ŏ´|‡morDkO\\mĆJfl@c̬¢aĦtRıҙ¾ùƀ^juųœK­ƒUFy™—Ɲ…›īÛ÷ąV×qƥV¿aȉd³B›qPBm›aËđŻģm“Å®Vйd^K‡KoŸnYg“¯Xhqa”Ldu¥•ÍpDž¡KąÅƒkĝęěhq‡}HyÓ]¹ǧ£…Í÷¿qáµ§š™g‘¤o^á¾ZE‡¤i`ij{n•ƒOl»ŸWÝĔįhg›F[¿¡—ßkOüš_‰€ū‹i„DZàUtėGylƒ}ŒÓM}€jpEC~¡FtoQi‘šHkk{Ãmï‚"
]
],
"encodeOffsets": [[[119712, 40641]], [[121616, 39981]], [[116462, 37237]]]
}
},
{
"type": "Feature",
"id": "140000",
"properties": {
"id": "140000",
"cp": [111.849248, 36.857014],
"name": "山西",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@Þĩ҃S‰ra}Á€yWix±Üe´lè“ßÓǏok‘ćiµVZģ¡coœ‘TS˹ĪmnÕńe–hZg{gtwªpXaĚThȑp{¶Eh—®RćƑP¿£‘Pmc¸mQÝW•ďȥoÅîɡųAďä³aωJ‘½¥PG­ąSM­™…EÅruµé€‘Yӎ•Ō_d›ĒCo­Èµ]¯_²ÕjāŽK~©ÅØ^ԛkïçămϑk]­±ƒcݯÑÃmQÍ~_a—pm…~ç¡q“ˆu{JÅŧ·Ls}–EyÁÆcI{¤IiCfUc•ƌÃp§]웫vD@¡SÀ‘µM‚ÅwuŽYY‡¡DbÑc¡hƒ×]nkoQdaMç~eD•ÛtT‰©±@¥ù@É¡‰ZcW|WqOJmĩl«ħşvOÓ«IqăV—¥ŸD[mI~Ó¢cehiÍ]Ɠ~ĥqXŠ·eƷœn±“}v•[ěďŽŕ]_‘œ•`‰¹ƒ§ÕōI™o©b­s^}Ét±ū«³p£ÿ·Wµ|¡¥ăFÏs׌¥ŅxŸÊdÒ{ºvĴÎêÌɊ²¶€ü¨|ÞƸµȲ‘LLúÉƎ¤ϊęĔV`„_bª‹S^|ŸdŠzY|dz¥p†ZbÆ£¶ÒK}tĦÔņƠ‚PYzn€ÍvX¶Ěn ĠÔ„zý¦ª˜÷žÑĸَUȌ¸‚dòÜJð´’ìúNM¬ŒXZ´‘¤ŊǸ_tldIš{¦ƀðĠȤ¥NehXnYG‚‡R° ƬDj¬¸|CĞ„Kq‚ºfƐiĺ©ª~ĆOQª ¤@ìǦɌ²æBŒÊ”TœŸ˜ʂōĖ’šĴŞ–ȀœÆÿȄlŤĒö„t”νî¼ĨXhŒ‘˜|ªM¤Ðz"
],
"encodeOffsets": [[116874, 41716]]
}
},
{
"type": "Feature",
"id": "150000",
"properties": {
"id": "150000",
"cp": [111.670801, 41.818311],
"name": "内蒙古",
"childNum": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
"@@¯PqƒFB…‰|S•³C|kñ•H‹d‘iÄ¥sˆʼnő…PóÑÑE^‘ÅPpy_YtS™hQ·aHwsOnʼnÚs©iqj›‰€USiº]ïWš‰«gW¡A–Rë¥_ŽsgÁnUI«m‰…„‹]j‡vV¼euhwqA„aW˜ƒ_µj…»çjioQR¹ēÃßt@r³[ÛlćË^ÍÉáG“›OUۗOB±•XŸkŇ¹£k|e]ol™ŸkVͼÕqtaÏõjgÁ£§U^Œ”RLˆËnX°Ç’Bz†^~wfvˆypV ¯„ƫĉ˭ȫƗŷɿÿĿƑ˃ĝÿÃǃßËőó©ǐȍŒĖM×ÍEyx‹þp]Évïè‘vƀnÂĴÖ@‚‰†V~Ĉv¦wĖt—ējyÄDXÄxGQuv_›i¦aBçw‘˛wD™©{ŸtāmQ€{EJ§KPśƘƿ¥@‰sCT•É}ɃwˆƇy±ŸgÑ“}T[÷kÐ禫…SÒ¥¸ëBX½‰HáŵÀğtSÝÂa[ƣ°¯¦P]£ġ“–“Òk®G²„èQ°óMq}EŠóƐÇ\\ƒ‡@áügQ͋u¥Fƒ“T՛¿Jû‡]|mvāÎYua^WoÀa·­ząÒot×¶CLƗi¯¤mƎHNJ¤îìɾŊìTdåwsRÖgĒųúÍġäÕ}Q¶—ˆ¿A•†‹[¡Œ{d×uQAƒ›M•xV‹vMOmăl«ct[wº_šÇʊŽŸjb£ĦS_é“QZ“_lwgOiýe`YYLq§IÁˆdz£ÙË[ÕªuƏ³ÍT—s·bÁĽäė[›b[ˆŗfãcn¥îC¿÷µ[ŏÀQ­ōšĉm¿Á^£mJVm‡—L[{Ï_£›F¥Ö{ŹA}…×Wu©ÅaųijƳhB{·TQqÙIķˑZđ©Yc|M¡…L•eVUóK_QWk’_ĥ‘¿ãZ•»X\\ĴuUƒè‡lG®ěłTĠğDєOrÍd‚ÆÍz]‹±…ŭ©ŸÅ’]ŒÅÐ}UË¥©Tċ™ïxgckfWgi\\ÏĒ¥HkµE˜ë{»ÏetcG±ahUiñiWsɁˆ·c–C‚Õk]wȑ|ća}w…VaĚ᠞ŒG°ùnM¬¯†{ÈˆÐÆA’¥ÄêJxÙ¢”hP¢Ûˆº€µwWOŸóFŽšÁz^ÀŗÎú´§¢T¤ǻƺSė‰ǵhÝÅQgvBHouʝl_o¿Ga{ïq{¥|ſĿHĂ÷aĝÇq‡Z‘ñiñC³ª—…»E`¨åXēÕqÉû[l•}ç@čƘóO¿¡ƒFUsA‰“ʽīccšocƒ‚ƒÇS}„“£‡IS~ălkĩXçmĈ…ŀЂoÐdxÒuL^T{r@¢‘žÍƒĝKén£kQ™‰yšÅõËXŷƏL§~}kqš»IHėDžjĝŸ»ÑÞoŸå°qTt|r©ÏS‹¯·eŨĕx«È[eMˆ¿yuˆ‘pN~¹ÏyN£{©’—g‹ħWí»Í¾s“əšDž_ÃĀɗ±ą™ijĉʍŌŷ—S›É“A‹±åǥɋ@럣R©ąP©}ĹªƏj¹erƒLDĝ·{i«ƫC£µsKCš…GS|úþX”gp›{ÁX¿Ÿć{ƱȏñZáĔyoÁhA™}ŅĆfdʼn„_¹„Y°ėǩÑ¡H¯¶oMQqð¡Ë™|‘Ñ`ƭŁX½·óۓxğįÅcQ‡ˆ“ƒs«tȋDžF“Ÿù^i‘t«Č¯[›hAi©á¥ÇĚ×l|¹y¯YȵƓ‹ñǙµï‚ċ™Ļ|Dœ™üȭ¶¡˜›oŽäÕG\\ďT¿Òõr¯œŸLguÏYęRƩšɷŌO\\İТæ^Ŋ IJȶȆbÜGŽĝ¬¿ĚVĎgª^íu½jÿĕęjık@Ľƒ]ėl¥Ë‡ĭûÁ„ƒėéV©±ćn©­ȇžÍq¯½•YÃÔʼn“ÉNѝÅÝy¹NqáʅDǡËñ­ƁYÅy̱os§ȋµʽǘǏƬɱà‘ưN¢ƔÊuľýľώȪƺɂļžxœZĈ}ÌʼnŪ˜ĺœŽĭFЛĽ̅ȣͽÒŵìƩÇϋÿȮǡŏçƑůĕ~Ǎ›¼ȳÐUf†dIxÿ\\G ˆzâɏÙOº·pqy£†@ŒŠqþ@Ǟ˽IBäƣzsÂZ†ÁàĻdñ°ŕzéØűzșCìDȐĴĺf®ŽÀľưø@ɜÖÞKĊŇƄ§‚͑těï͡VAġÑÑ»d³öǍÝXĉĕÖ{þĉu¸ËʅğU̎éhɹƆ̗̮ȘNJ֥ड़ࡰţાíϲäʮW¬®ҌeרūȠkɬɻ̼ãüfƠSצɩςåȈHϚÎKdzͲOðÏȆƘ¼CϚǚ࢚˼ФԂ¤ƌžĞ̪Qʤ´¼mȠJˀŸƲÀɠmǐnǔĎȆÞǠN~€ʢĜ‚¶ƌĆĘźʆȬ˪ĚǏĞGȖƴƀj`ĢçĶāàŃºē̃ĖćšYŒÀŎüôQÐÂŎŞdžŞêƖš˜oˆDĤÕºÑǘÛˤ³̀gńƘĔÀ^žªƂ`ªt¾äƚêĦĀ¼Ð€Ĕǎ¨Ȕ»͠^ˮÊȦƤøxRrŜH¤¸ÂxDĝŒ|ø˂˜ƮÐ¬ɚwɲFjĔ²Äw°dždÀɞ_ĸdîàŎjʜêTĞªŌ‡ŜWÈ|tqĢUB~´°ÎFC•ŽU¼pĀēƄN¦¾O¶ŠłKĊOj“Ě”j´ĜYp˜{¦„ˆSĚÍ\\Tš×ªV–÷Ší¨ÅDK°ßtŇĔKš¨ǵÂcḷ̌ĚǣȄĽF‡lġUĵœŇ‹ȣFʉɁƒMğįʏƶɷØŭOǽ«ƽū¹Ʊő̝Ȩ§ȞʘĖiɜɶʦ}¨֪ࠜ̀ƇǬ¹ǨE˦ĥªÔêFŽxúQ„Er´W„rh¤Ɛ \\talĈDJ˜Ü|[Pll̚¸ƎGú´Pž¬W¦†^¦–H]prR“n|or¾wLVnÇIujkmon£cX^Bh`¥V”„¦U¤¸}€xRj–[^xN[~ªŠxQ„‚[`ªHÆÂExx^wšN¶Ê˜|¨ì†˜€MrœdYp‚oRzNy˜ÀDs~€bcfÌ`L–¾n‹|¾T‚°c¨È¢a‚r¤–`[|òDŞĔöxElÖdH„ÀI`„Ď\\Àì~ƎR¼tf•¦^¢ķ¶e”ÐÚMŒptgj–„ɡČÅyġLû™ŇV®ŠÄÈƀ†Ď°P|ªVV†ªj–¬ĚÒêp¬–E|ŬÂc|ÀtƐK fˆ{ĘFǜƌXƲąo½Ę‘\\¥–o}›Ûu£ç­kX‘{uĩ«āíÓUŅßŢq€Ť¥lyň[€oi{¦‹L‡ń‡ðFȪȖ”ĒL„¿Ì‹ˆfŒ£K£ʺ™oqNŸƒwğc`ue—tOj×°KJ±qƒÆġm‰Ěŗos¬…qehqsuœƒH{¸kH¡Š…ÊRǪÇƌbȆ¢´ä܍¢NìÉʖ¦â©Ġu¦öČ^â£Ăh–šĖMÈÄw‚\\fŦ°W ¢¾luŸD„wŠ\\̀ʉÌÛM…Ā[bӞEn}¶Vc…ê“sƒ"
]
],
"encodeOffsets": [[[129102, 52189]]]
}
},
{
"type": "Feature",
"id": "210000",
"properties": {
"id": "210000",
"cp": [123.429096, 41.796767],
"name": "辽宁",
"childNum": 16
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@L–Ž@@s™a"],
["@@MnNm"],
["@@d‚c"],
["@@eÀ‚C@b‚“‰"],
["@@f‡…Xwkbr–Ä`qg"],
["@@^jtW‘Q"],
["@@~ Y]c"],
["@@G`ĔN^_¿Z‚ÃM"],
["@@iX¶B‹Y"],
["@@„YƒZ"],
["@@L_{Epf"],
["@@^WqCT\\"],
["@@\\[“‹§t|”¤_"],
["@@m`n_"],
["@@Ïxnj{q_×^Giip"],
[
"@@@œé^B†‡ntˆaÊU—˜Ÿ]x ¯ÄPIJ­°h€ʙK³†VˆÕ@Y~†|EvĹsDŽ¦­L^p²ŸÒG ’Ël]„xxÄ_˜fT¤Ď¤cŽœP„–C¨¸TVjbgH²sdÎdHt`Bˆ—²¬GJję¶[ÐhjeXdlwhšðSȦªVÊπ‹Æ‘Z˜ÆŶ®²†^ŒÎyÅÎcPqń“ĚDMħĜŁH­ˆk„çvV[ij¼W–‚YÀäĦ’‘`XlžR`žôLUVžfK–¢†{NZdĒª’YĸÌÚJRr¸SA|ƴgŴĴÆbvªØX~†źBŽ|¦ÕœEž¤Ð`\\|Kˆ˜UnnI]¤ÀÂĊnŎ™R®Ő¿¶\\ÀøíDm¦ÎbŨab‰œaĘ\\ľã‚¸a˜tÎSƐ´©v\\ÖÚÌǴ¤Â‡¨JKr€Z_Z€fjþhPkx€`Y”’RIŒjJcVf~sCN¤ ˆE‚œhæm‰–sHy¨SðÑÌ\\\\ŸĐRZk°IS§fqŒßýáЍÙÉÖ[^¯ǤŲ„ê´\\¦¬ĆPM¯£Ÿˆ»uïpùzEx€žanµyoluqe¦W^£ÊL}ñrkqWňûP™‰UP¡ôJŠoo·ŒU}£Œ„[·¨@XŒĸŸ“‹‹DXm­Ûݏº‡›GU‹CÁª½{íĂ^cj‡k“¶Ã[q¤“LÉö³cux«zZfƒ²BWÇ®Yß½ve±ÃC•ý£W{Ú^’q^sÑ·¨‹ÍOt“¹·C¥‡GD›rí@wÕKţ݋˜Ÿ«V·i}xËÍ÷‘i©ĝ‡ɝǡ]ƒˆ{c™±OW‹³Ya±Ÿ‰_穂Hžĕoƫ€Ňqƒr³‰Lys[„ñ³¯OS–ďOMisZ†±ÅFC¥Pq{‚Ã[Pg}\\—¿ghćO…•k^ģÁFıĉĥM­oEqqZûěʼn³F‘¦oĵ—hŸÕP{¯~TÍlª‰N‰ßY“Ð{Ps{ÃVU™™eĎwk±ʼnVÓ½ŽJãÇÇ»Jm°dhcÀff‘dF~ˆ€ĀeĖ€d`sx² šƒ®EżĀdQ‹Âd^~ăÔHˆ¦\\›LKpĄVez¤NP ǹӗR™ÆąJSh­a[¦´Âghwm€BÐ¨źhI|žVVŽ—Ž|p] Â¼èNä¶ÜBÖ¼“L`‚¼bØæŒKV”ŸpoœúNZÞÒKxpw|ÊEMnzEQšŽIZ”ŽZ‡NBˆčÚFÜçmĩ‚WĪñt‘ÞĵÇñZ«uD‚±|Əlij¥ãn·±PmÍa‰–da‡ CL‡Ǒkùó¡³Ï«QaċϑOÃ¥ÕđQȥċƭy‹³ÃA"
]
],
"encodeOffsets": [
[[123686, 41445]],
[[126019, 40435]],
[[124393, 40128]],
[[126117, 39963]],
[[125322, 40140]],
[[126686, 40700]],
[[126041, 40374]],
[[125584, 40168]],
[[125453, 40165]],
[[125362, 40214]],
[[125280, 40291]],
[[125774, 39997]],
[[125976, 40496]],
[[125822, 39993]],
[[125509, 40217]],
[[122731, 40949]]
]
}
},
{
"type": "Feature",
"id": "220000",
"properties": { "id": "220000", "cp": [125.3245, 43.886841], "name": "吉林", "childNum": 1 },
"geometry": {
"type": "Polygon",
"coordinates": [
"@@‘p䔳PClƒFbbÍzš€wBG’ĭ€Z„Åi“»ƒlY­ċ²SgŽkÇ£—^S‰“qd¯•‹R…©éŽ£¯S†\\cZ¹iűƏCuƍÓX‡oR}“M^o•£…R}oªU­F…uuXHlEŕ‡€Ï©¤ÛmTŽþ¤D–²ÄufàÀ­XXȱAe„yYw¬dvõ´KÊ£”\\rµÄl”iˆdā]|DÂVŒœH¹ˆÞ®ÜWnŒC”Œķ W‹§@\\¸‹ƒ~¤‹Vp¸‰póIO¢ŠVOšŇürXql~òÉK]¤¥Xrfkvzpm¶bwyFoúvð‡¼¤ N°ąO¥«³[ƒéǡű_°Õ\\ÚÊĝŽþâőàerR¨­JYlďQ[ ÏYëЧTGz•tnŠß¡gFkMŸāGÁ¤ia É‰™È¹`\\xs€¬dĆkNnuNUŠ–užP@‚vRY¾•–\\¢…ŒGªóĄ~RãÖÎĢù‚đŴÕhQŽxtcæëSɽʼníëlj£ƍG£nj°KƘµDsØÑpyƸ®¿bXp‚]vbÍZuĂ{nˆ^IüœÀSք”¦EŒvRÎûh@℈[‚Əȉô~FNr¯ôçR±ƒ­HÑl•’Ģ–^¤¢‚OðŸŒævxsŒ]ÞÁTĠs¶¿âƊGW¾ìA¦·TѬ†è¥€ÏÐJ¨¼ÒÖ¼ƒƦɄxÊ~S–tD@ŠĂ¼Ŵ¡jlºWžvЉˆzƦZЎ²CH— „Axiukd‹ŒGgetqmcžÛ£Ozy¥cE}|…¾cZ…k‚‰¿uŐã[oxGikfeäT@…šSUwpiÚFM©’£è^ڟ‚`@v¶eň†f h˜eP¶žt“äOlÔUgƒÞzŸU`lœ}ÔÆUvØ_Ō¬Öi^ĉi§²ÃŠB~¡Ĉ™ÚEgc|DC_Ȧm²rBx¼MÔ¦ŮdĨÃâYx‘ƘDVÇĺĿg¿cwÅ\\¹˜¥Yĭlœ¤žOv†šLjM_a W`zļMž·\\swqÝSA‡š—q‰Śij¯Š‘°kŠRē°wx^Đkǂғ„œž“œŽ„‹\\]˜nrĂ}²ĊŲÒøãh·M{yMzysěnĒġV·°“G³¼XÀ““™¤¹i´o¤ŃšŸÈ`̃DzÄUĞd\\i֚ŒˆmÈBĤÜɲDEh LG¾ƀľ{WaŒYÍȏĢĘÔRîĐj‹}Ǟ“ccj‡oUb½š{“h§Ǿ{K‹ƖµÎ÷žGĀÖŠåưÎs­l›•yiē«‹`姝H¥Ae^§„GK}iã\\c]v©ģZ“mÃ|“[M}ģTɟĵ‘Â`À–çm‰‘FK¥ÚíÁbXš³ÌQґHof{‰]e€pt·GŋĜYünĎųVY^’˜ydõkÅZW„«WUa~U·Sb•wGçǑ‚“iW^q‹F‚“›uNĝ—·Ew„‹UtW·Ýďæ©PuqEzwAV•—XR‰ãQ`­©GŒM‡ehc›c”ďϝd‡©ÑW_ϗYƅŒ»…é\\ƒɹ~ǙG³mØ©BšuT§Ĥ½¢Ã_ý‘L¡‘ýŸqT^rme™\\Pp•ZZbƒyŸ’uybQ—efµ]UhĿDCmûvašÙNSkCwn‰cćfv~…Y‹„ÇG"
],
"encodeOffsets": [[130196, 42528]]
}
},
{
"type": "Feature",
"id": "230000",
"properties": {
"id": "230000",
"cp": [128.642464, 46.756967],
"name": "黑龙江",
"childNum": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
"@@UƒµNÿ¥īè灋•HÍøƕ¶LŒǽ|g¨|”™Ža¾pViˆdd”~ÈiŒíďÓQġėǐZ΋ŽXb½|ſÃH½ŸKFgɱCģÛÇA‡n™‹jÕc[VĝDZÃ˄Ç_™ £ń³pŽj£º”š¿”»WH´¯”U¸đĢmžtĜyzzNN|g¸÷äűѱĉā~mq^—Œ[ƒ”››”ƒǁÑďlw]¯xQĔ‰¯l‰’€°řĴrŠ™˜BˆÞTxr[tޏĻN_yŸX`biN™Ku…P›£k‚ZĮ—¦[ºxÆÀdhŽĹŀUÈƗCw’áZħÄŭcÓ¥»NAw±qȥnD`{ChdÙFćš}¢‰A±Äj¨]ĊÕjŋ«×`VuÓś~_kŷVÝyh„“VkÄãPs”Oµ—fŸge‚Ň…µf@u_Ù ÙcŸªNªÙEojVx™T@†ãSefjlwH\\pŏäÀvŠŽlY†½d{†F~¦dyz¤PÜndsrhf‹HcŒvlwjFœ£G˜±DύƥY‡yϊu¹XikĿ¦ÏqƗǀOŜ¨LI|FRĂn sª|Cš˜zxAè¥bœfudTrFWÁ¹Am|˜ĔĕsķÆF‡´Nš‰}ć…UŠÕ@Áijſmužç’uð^ÊýowŒFzØÎĕNőžǏȎôªÌŒDŽàĀÄ˄ĞŀƒʀĀƘŸˮȬƬĊ°ƒUŸzou‡xe]}Ž…AyȑW¯ÌmK‡“Q]‹Īºif¸ÄX|sZt|½ÚUΠlkš^p{f¤lˆºlÆW –€A²˜PVܜPH”Êâ]ÎĈÌÜk´\\@qàsĔÄQºpRij¼èi†`¶—„bXƒrBgxfv»ŽuUiˆŒ^v~”J¬mVp´£Œ´VWrnP½ì¢BX‚¬h™ŠðX¹^TjVœŠriªj™tŊÄm€tPGx¸bgRšŽsT`ZozÆO]’ÒFô҆Oƒ‡ŊŒvŞ”p’cGŒêŠsx´DR–Œ{A†„EOr°Œ•žx|íœbˆ³Wm~DVjºéNN†Ëܲɶ­GƒxŷCStŸ}]ûō•SmtuÇÃĕN•™āg»šíT«u}ç½BĵÞʣ¥ëÊ¡Mێ³ãȅ¡ƋaǩÈÉQ‰†G¢·lG|›„tvgrrf«†ptęŘnŠÅĢr„I²¯LiØsPf˜_vĠd„xM prʹšL¤‹¤‡eˌƒÀđK“žïÙVY§]I‡óáĥ]ķ†Kˆ¥Œj|pŇ\\kzţ¦šnņäÔVĂîά|vW’®l¤èØr‚˜•xm¶ă~lÄƯĄ̈́öȄEÔ¤ØQĄ–Ą»ƢjȦOǺ¨ìSŖÆƬy”Qœv`–cwƒZSÌ®ü±DŽ]ŀç¬B¬©ńzƺŷɄeeOĨS’Œfm Ċ‚ƀP̎ēz©Ċ‚ÄÕÊmgŸÇsJ¥ƔˆŊśæ’΁Ñqv¿íUOµª‰ÂnĦÁ_½ä@ê텣P}Ġ[@gġ}g“ɊדûÏWXá¢užƻÌsNͽƎÁ§č՛AēeL³àydl›¦ĘVçŁpśdžĽĺſʃQíÜçÛġԏsĕ¬—Ǹ¯YßċġHµ ¡eå`ļƒrĉŘóƢFì“ĎWøxÊk†”ƈdƬv|–I|·©NqńRŀƒ¤é”eŊœŀ›ˆàŀU²ŕƀB‚Q£Ď}L¹Îk@©ĈuǰųǨ”Ú§ƈnTËÇéƟÊcfčŤ^Xm‡—HĊĕË«W·ċëx³ǔķÐċJā‚wİ_ĸ˜Ȁ^ôWr­°oú¬Ħ…ŨK~”ȰCĐ´Ƕ£’fNÎèâw¢XnŮeÂÆĶŽ¾¾xäLĴĘlļO¤ÒĨA¢Êɚ¨®‚ØCÔ ŬGƠ”ƦYĜ‡ĘÜƬDJ—g_ͥœ@čŅĻA“¶¯@wÎqC½Ĉ»NŸăëK™ďÍQ“Ùƫ[«Ãí•gßÔÇOÝáW‘ñuZ“¯ĥ€Ÿŕā¡ÑķJu¤E Ÿå¯°WKɱ_d_}}vyŸõu¬ï¹ÓU±½@gÏ¿rýD‰†g…Cd‰µ—°MFYxw¿CG£‹Rƛ½Õ{]L§{qqąš¿BÇƻğëšܭNJË|c²}Fµ}›ÙRsÓpg±ŠQNqǫŋRwŕnéÑÉKŸ†«SeYR…ŋ‹@{¤SJ}šD Ûǖ֍Ÿ]gr¡µŷjqWÛham³~S«“„›Þ]"
]
],
"encodeOffsets": [[[134456, 44547]]]
}
},
{
"type": "Feature",
"id": "320000",
"properties": {
"id": "320000",
"cp": [119.767413, 33.041544],
"name": "江苏",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@cþÅPiŠ`ZŸRu¥É\\]~°ŽY`µ†Óƒ^phÁbnÀşúŽòa–ĬºTÖŒb‚˜e¦¦€{¸ZâćNpŒ©žHr|^ˆmjhŠSEb\\afv`sz^lkŽlj‹Ätg‹¤D˜­¾Xš¿À’|ДiZ„ȀåB·î}GL¢õcßjaŸyBFµÏC^ĭ•cÙt¿sğH]j{s©HM¢ƒQnDÀ©DaÜތ·jgàiDbPufjDk`dPOîƒhw¡ĥ‡¥šG˜ŸP²ĐobºrY†„î¶aHŢ´ ]´‚rılw³r_{£DB_Ûdåuk|ˆŨ¯F Cºyr{XFy™e³Þċ‡¿Â™kĭB¿„MvÛpm`rÚã”@ƹhågËÖƿxnlč¶Åì½Ot¾dJlŠVJʜǀœŞqvnOŠ^ŸJ”Z‘ż·Q}ê͎ÅmµÒ]Žƍ¦Dq}¬R^èĂ´ŀĻĊIԒtžIJyQŐĠMNtœR®òLh‰›Ěs©»œ}OӌGZz¶A\\jĨFˆäOĤ˜HYš†JvÞHNiÜaϚɖnFQlšNM¤ˆB´ĄNöɂtp–Ŭdf先‹qm¿QûŠùއÚb¤uŃJŴu»¹Ą•lȖħŴw̌ŵ²ǹǠ͛hĭłƕrçü±Y™xci‡tğ®jű¢KOķ•Coy`å®VTa­_Ā]ŐÝɞï²ʯÊ^]afYǸÃĆēĪȣJđ͍ôƋĝÄ͎ī‰çÛɈǥ£­ÛmY`ó£Z«§°Ó³QafusNıDž_k}¢m[ÝóDµ—¡RLčiXy‡ÅNïă¡¸iĔϑNÌŕoēdōîåŤûHcs}~Ûwbù¹£¦ÓCt‹OPrƒE^ÒoŠg™ĉIµžÛÅʹK…¤½phMŠü`o怆ŀ"
],
"encodeOffsets": [[121740, 32276]]
}
},
{
"type": "Feature",
"id": "330000",
"properties": {
"id": "330000",
"cp": [120.153576, 29.287459],
"name": "浙江",
"childNum": 45
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@E^dQ]K"],
["@@jX^j‡"],
["@@sfŠbU‡"],
["@@qP\\xz[ck"],
["@@‘Rƒ¢‚FX}°[s_"],
["@@Cbœ\\—}"],
["@@e|v\\la{u"],
["@@v~u}"],
["@@QxÂF¯}"],
["@@¹nŒvÞs¯o"],
["@@rSkUEj"],
["@@bi­ZŒP"],
["@@p[}INf"],
["@@À¿€"],
["@@¹dnbŒ…"],
["@@rSŸBnR"],
["@@g~h}"],
["@@FlEk"],
["@@OdPc"],
["@@v[u\\"],
["@@FjâL~wyoo~›sµL–\\"],
["@@¬e¹aNˆ"],
["@@\\nÔ¡q]L³ë\\ÿ®ŒQ֎"],
["@@ÊA­©[¬"],
["@@KxŒv­"],
["@@@hlIk]"],
["@@pW{o||j"],
["@@Md|_mC"],
["@@¢…X£ÏylD¼XˆtH"],
["@@hlÜ[LykAvyfw^Ež›¤"],
["@@fp¤Mus“R"],
["@@®_ma~•LÁ¬šZ"],
["@@iM„xZ"],
["@@ZcYd"],
["@@Z~dOSo|A¿qZv"],
["@@@`”EN¡v"],
["@@|–TY{"],
["@@@n@m"],
["@@XWkCT\\"],
["@@ºwšZRkĕWO¢"],
["@@™X®±Grƪ\\ÔáXq{‹"],
["@@ůTG°ĄLHm°UC‹"],
[
"@@¤Ž€aÜx~}dtüGæţŎíĔcŖpMËВj碷ðĄÆMzˆjWKĎ¢Q¶˜À_꒔_Bı€i«pZ€gf€¤Nrq]§ĂN®«H±‡yƳí¾×ŸīàLłčŴǝĂíÀBŖÕªˆŠÁŖHŗʼnåqûõi¨hÜ·ƒñt»¹ýv_[«¸m‰YL¯‰Qª…mĉÅdMˆ•gÇjcº«•ęœ¬­K­´ƒB«Âącoċ\\xKd¡gěŧ«®á’[~ıxu·Å”KsËɏc¢Ù\\ĭƛëbf¹­ģSƒĜkáƉÔ­ĈZB{ŠaM‘µ‰fzʼnfåÂŧįƋǝÊĕġć£g³ne­ą»@­¦S®‚\\ßðCšh™iqªĭiAu‡A­µ”_W¥ƣO\\lċĢttC¨£t`ˆ™PZäuXßBs‡Ļyek€OđġĵHuXBšµ]׌‡­­\\›°®¬F¢¾pµ¼kŘó¬Wät’¸|@ž•L¨¸µr“ºù³Ù~§WI‹ŸZWŽ®’±Ð¨ÒÉx€`‰²pĜ•rOògtÁZ}þÙ]„’¡ŒŸFK‚wsPlU[}¦Rvn`hq¬\\”nQ´ĘRWb”‚_ rtČFI֊kŠŠĦPJ¶ÖÀÖJĈĄTĚòžC ²@Pú…Øzœ©PœCÈÚœĒ±„hŖ‡l¬â~nm¨f©–iļ«m‡nt–u†ÖZÜÄj“ŠLŽ®E̜Fª²iÊxبžIÈhhst"
],
["@@o\\V’zRZ}y"],
["@@†@°¡mۛGĕ¨§Ianá[ýƤjfæ‡ØL–•äGr™"]
],
"encodeOffsets": [
[[125592, 31553]],
[[125785, 31436]],
[[125729, 31431]],
[[125513, 31380]],
[[125223, 30438]],
[[125115, 30114]],
[[124815, 29155]],
[[124419, 28746]],
[[124095, 28635]],
[[124005, 28609]],
[[125000, 30713]],
[[125111, 30698]],
[[125078, 30682]],
[[125150, 30684]],
[[124014, 28103]],
[[125008, 31331]],
[[125411, 31468]],
[[125329, 31479]],
[[125626, 30916]],
[[125417, 30956]],
[[125254, 30976]],
[[125199, 30997]],
[[125095, 31058]],
[[125083, 30915]],
[[124885, 31015]],
[[125218, 30798]],
[[124867, 30838]],
[[124755, 30788]],
[[124802, 30809]],
[[125267, 30657]],
[[125218, 30578]],
[[125200, 30562]],
[[124968, 30474]],
[[125167, 30396]],
[[124955, 29879]],
[[124714, 29781]],
[[124762, 29462]],
[[124325, 28754]],
[[123990, 28459]],
[[125366, 31477]],
[[125115, 30363]],
[[125369, 31139]],
[[122495, 31878]],
[[125329, 30690]],
[[125192, 30787]]
]
}
},
{
"type": "Feature",
"id": "340000",
"properties": { "id": "340000", "cp": [117.283042, 31.26119], "name": "安徽", "childNum": 3 },
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@^iuLX^"],
["@@‚e©Ehl"],
[
"@@°ZÆëϵmkǀwÌÕæhºgBĝâqÙĊz›ÖgņtÀÁÊÆá’hEz|WzqD¹€Ÿ°E‡ŧl{ævÜcA`¤C`|´qžxIJkq^³³ŸGšµbƒíZ…¹qpa±ď OH—¦™Ħˆx¢„gPícOl_iCveaOjCh߸i݋bÛªCC¿€m„RV§¢A|t^iĠGÀtÚs–d]ĮÐDE¶zAb àiödK¡~H¸íæAžǿYƒ“j{ď¿‘™À½W—®£ChŒÃsiŒkkly]_teu[bFa‰Tig‡n{]Gqªo‹ĈMYá|·¥f¥—őaSÕė™NµñĞ«ImŒ_m¿Âa]uĜp …Z_§{Cƒäg¤°r[_Yj‰ÆOdý“[ŽI[á·¥“Q_n‡ùgL¾mv™ˊBÜÆ¶ĊJhšp“c¹˜O]iŠ]œ¥ jtsggJǧw×jÉ©±›EFˍ­‰Ki”ÛÃÕYv…s•ˆm¬njĻª•§emná}k«ŕˆƒgđ²Ù›DǤ›í¡ªOy›†×Où±@DŸñSęćăÕIÕ¿IµĥO‰‰jNÕËT¡¿tNæŇàåyķrĕq§ÄĩsWÆßŽF¶žX®¿‰mŒ™w…RIޓfßoG‘³¾©uyH‘į{Ɓħ¯AFnuP…ÍÔzšŒV—dàôº^Ðæd´€‡oG¤{S‰¬ćxã}›ŧ×Kǥĩ«žÕOEзÖdÖsƘѨ[’Û^Xr¢¼˜§xvěƵ`K”§ tÒ´Cvlo¸fzŨð¾NY´ı~ÉĔē…ßúLÃϖ_ÈÏ|]ÂÏFl”g`bšežž€n¾¢pU‚h~ƴ˶_‚r sĄ~cž”ƈ]|r c~`¼{À{ȒiJjz`îÀT¥Û³…]’u}›f…ïQl{skl“oNdŸjŸäËzDvčoQŠďHI¦rb“tHĔ~BmlRš—V_„ħTLnñH±’DžœL‘¼L˜ªl§Ťa¸ŒĚlK²€\\RòvDcÎJbt[¤€D@®hh~kt°ǾzÖ@¾ªdb„YhüóZ ň¶vHrľ\\ʗJuxAT|dmÀO„‹[ÃԋG·ĚąĐlŪÚpSJ¨ĸˆLvÞcPæķŨŽ®mАˆálŸwKhïgA¢ųƩޖ¤OȜm’°ŒK´"
]
],
"encodeOffsets": [[[121722, 32278]], [[119475, 30423]], [[119168, 35472]]]
}
},
{
"type": "Feature",
"id": "350000",
"properties": {
"id": "350000",
"cp": [118.306239, 26.075302],
"name": "福建",
"childNum": 18
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@“zht´‡]"],
["@@aj^~ĆG—©O"],
["@@ed¨„C}}i"],
["@@@vˆPGsQ"],
["@@‰sBz‚ddW]Q"],
["@@SލQ“{"],
["@@NŽVucW"],
["@@qptBAq"],
["@@‰’¸[mu"],
["@@Q\\pD]_"],
["@@jSwUadpF"],
["@@eXª~ƒ•"],
["@@AjvFso"],
["@@fT–›_Çí\\Ÿ™—v|ba¦jZÆy€°"],
["@@IjJi"],
["@@wJI€ˆxš«¼AoNe{M­"],
["@@K‰±¡Óˆ”ČäeZ"],
[
"@@k¡¹Eh~c®wBk‹UplÀ¡I•~Māe£bN¨gZý¡a±Öcp©PhžI”Ÿ¢Qq…ÇGj‹|¥U™ g[Ky¬ŏ–v@OpˆtÉEŸF„\\@ åA¬ˆV{Xģ‰ĐBy…cpě…¼³Ăp·¤ƒ¥o“hqqÚ¡ŅLsƒ^ᗞ§qlŸÀhH¨MCe»åÇGD¥zPO£čÙkJA¼ß–ėu›ĕeûҍiÁŧSW¥˜QŠûŗ½ùěcݧSùĩąSWó«íęACµ›eR—åǃRCÒÇZÍ¢‹ź±^dlsŒtjD¸•‚ZpužÔâÒH¾oLUêÃÔjjēò´ĄW‚ƛ…^Ñ¥‹ĦŸ@Çò–ŠmŒƒOw¡õyJ†yD}¢ďÑÈġfŠZd–a©º²z£šN–ƒjD°Ötj¶¬ZSÎ~¾c°¶Ðm˜x‚O¸¢Pl´žSL|¥žA†ȪĖM’ņIJg®áIJČĒü` ŽQF‡¬h|ÓJ@zµ |ê³È ¸UÖŬŬÀEttĸr‚]€˜ðŽM¤ĶIJHtÏ A’†žĬkvsq‡^aÎbvŒd–™fÊòSD€´Z^’xPsÞrv‹ƞŀ˜jJd×ŘÉ ®A–ΦĤd€xĆqAŒ†ZR”ÀMźŒnĊ»ŒİÐZ— YX–æJŠyĊ²ˆ·¶q§·–K@·{s‘Xãô«lŗ¶»o½E¡­«¢±¨Yˆ®Ø‹¶^A™vWĶGĒĢžPlzfˆļŽtàAvWYãšO_‡¤sD§ssČġ[kƤPX¦Ž`¶“ž®ˆBBvĪjv©šjx[L¥àï[F…¼ÍË»ğV`«•Ip™}ccÅĥZE‹ãoP…´B@ŠD—¸m±“z«Ƴ—¿å³BRضˆœWlâþäą`“]Z£Tc— ĹGµ¶H™m@_©—kŒ‰¾xĨ‡ôȉðX«½đCIbćqK³Á‹Äš¬OAwã»aLʼn‡ËĥW[“ÂGI—ÂNxij¤D¢ŽîĎÎB§°_JœGsƒ¥E@…¤uć…P‘å†cuMuw¢BI¿‡]zG¹guĮck\\_"
]
],
"encodeOffsets": [
[[123250, 27563]],
[[122541, 27268]],
[[123020, 27189]],
[[122916, 27125]],
[[122887, 26845]],
[[122808, 26762]],
[[122568, 25912]],
[[122778, 26197]],
[[122515, 26757]],
[[122816, 26587]],
[[123388, 27005]],
[[122450, 26243]],
[[122578, 25962]],
[[121255, 25103]],
[[120987, 24903]],
[[122339, 25802]],
[[121042, 25093]],
[[122439, 26024]]
]
}
},
{
"type": "Feature",
"id": "360000",
"properties": {
"id": "360000",
"cp": [115.592151, 27.676493],
"name": "江西",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@ĢĨƐgÂMD~ņªe^\\^§„ý©j׍cZ†Ø¨zdÒa¶ˆlҍJŒìõ`oz÷@¤u޸´†ôęöY¼‰HČƶajlÞƩ¥éZ[”|h}^U Œ ¥p„ĄžƦO lt¸Æ €Q\\€ŠaÆ|CnÂOjt­ĚĤd’ÈŒF`’¶„@Ð딠¦ōҞ¨Sêv†HĢûXD®…QgėWiØPÞìºr¤dž€NĠ¢l–•ĄtZoœCƞÔºCxrpĠV®Ê{f_Y`_ƒeq’’®Aot`@o‚DXfkp¨|Šs¬\\D‘ÄSfè©Hn¬…^DhÆyøJh“ØxĢĀLʈ„ƠPżċĄwȠ̦G®ǒĤäTŠÆ~ĦwŠ«|TF¡Šn€c³Ïå¹]ĉđxe{ÎӐ†vOEm°BƂĨİ|G’vz½ª´€H’àp”eJ݆Qšxn‹ÀŠW­žEµàXÅĪt¨ÃĖrÄwÀFÎ|ňÓMå¼ibµ¯»åDT±m[“r«_gŽmQu~¥V\\OkxtL E¢‹ƒ‘Ú^~ýê‹Pó–qo슱_Êw§ÑªåƗ⼋mĉŹ‹¿NQ“…YB‹ąrwģcÍ¥B•Ÿ­ŗÊcØiI—žƝĿuŒqtāwO]‘³YCñTeɕš‹caub͈]trlu€ī…B‘ПGsĵıN£ï—^ķqss¿FūūV՟·´Ç{éĈý‰ÿ›OEˆR_ŸđûIċâJh­ŅıN‘ȩĕB…¦K{Tk³¡OP·wn—µÏd¯}½TÍ«YiµÕsC¯„iM•¤™­•¦¯P|ÿUHv“he¥oFTu‰õ\\ŽOSs‹MòđƇiaºćXŸĊĵà·çhƃ÷ǜ{‘ígu^›đg’m[×zkKN‘¶Õ»lčÓ{XSƉv©_ÈëJbVk„ĔVÀ¤P¾ºÈMÖxlò~ªÚàGĂ¢B„±’ÌŒK˜y’áV‡¼Ã~­…`g›ŸsÙfI›Ƌlę¹e|–~udjˆuTlXµf`¿JdŠ[\\˜„L‚‘²"
],
"encodeOffsets": [[116689, 26234]]
}
},
{
"type": "Feature",
"id": "370000",
"properties": {
"id": "370000",
"cp": [118.000923, 36.275807],
"name": "山东",
"childNum": 13
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@Xjd]{K"],
["@@itbFHy"],
["@@HlGk"],
["@@T‚ŒGŸy"],
["@@K¬˜•‹U"],
["@@WdXc"],
["@@PtOs"],
["@@•LnXhc"],
["@@ppVƒu]Or"],
["@@cdzAUa"],
["@@udRhnCI‡"],
["@@ˆoIƒpR„"],
[
"@@Ľč{fzƤî’Kš–ÎMĮ]†—ZFˆ½Y]â£ph’™š¶¨râøÀ†ÎǨ¤^ºÄ”Gzˆ~grĚĜlĞÆ„LĆdž¢Îo¦–cv“Kb€gr°Wh”mZp ˆL]LºcU‰Æ­n”żĤÌǜbAnrOAœ´žȊcÀbƦUØrĆUÜøœĬƞ†š˜Ez„VL®öØBkŖÝĐ˹ŧ̄±ÀbÎɜnb²ĦhņBĖ›žįĦåXćì@L¯´ywƕCéõė ƿ¸‘lµ¾Z|†ZWyFYŸ¨Mf~C¿`€à_RÇzwƌfQnny´INoƬˆèôº|sT„JUš›‚L„îVj„ǎ¾Ē؍‚Dz²XPn±ŴPè¸ŔLƔÜƺ_T‘üÃĤBBċȉöA´fa„˜M¨{«M`‡¶d¡ô‰Ö°šmȰBÔjjŒ´PM|”c^d¤u•ƒ¤Û´Œä«ƢfPk¶Môlˆ]Lb„}su^ke{lC‘…M•rDŠÇ­]NÑFsmoõľH‰yGă{{çrnÓE‰‹ƕZGª¹Fj¢ïW…uøCǷ돡ąuhÛ¡^Kx•C`C\\bÅxì²ĝÝ¿_N‰īCȽĿåB¥¢·IŖÕy\\‡¹kx‡Ã£Č×GDyÕ¤ÁçFQ¡„KtŵƋ]CgÏAùSed‡cÚź—ŠuYfƒyMmhUWpSyGwMPqŀ—›Á¼zK›¶†G•­Y§Ëƒ@–´śÇµƕBmœ@Io‚g——Z¯u‹TMx}C‘‰VK‚ï{éƵP—™_K«™pÛÙqċtkkù]gŽ‹Tğwo•ɁsMõ³ă‡AN£™MRkmEʕč™ÛbMjÝGu…IZ™—GPģ‡ãħE[iµBEuŸDPԛ~ª¼ętŠœ]ŒûG§€¡QMsğNPŏįzs£Ug{đJĿļā³]ç«Qr~¥CƎÑ^n¶ÆéÎR~ݏY’I“] P‰umŝrƿ›‰›Iā‹[x‰edz‹L‘¯v¯s¬ÁY…~}…ťuٌg›ƋpÝĄ_ņī¶ÏSR´ÁP~ž¿Cyžċßdwk´Ss•X|t‰`Ä Èð€AªìÎT°¦Dd–€a^lĎDĶÚY°Ž`ĪŴǒˆ”àŠv\\ebŒZH„ŖR¬ŢƱùęO•ÑM­³FۃWp[ƒ"
]
],
"encodeOffsets": [
[[123806, 39303]],
[[123821, 39266]],
[[123742, 39256]],
[[123702, 39203]],
[[123649, 39066]],
[[123847, 38933]],
[[123580, 38839]],
[[123894, 37288]],
[[123043, 36624]],
[[123344, 38676]],
[[123522, 38857]],
[[123628, 38858]],
[[118260, 36742]]
]
}
},
{
"type": "Feature",
"id": "410000",
"properties": {
"id": "410000",
"cp": [113.665412, 33.757975],
"name": "河南",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@•ýL™ùµP³swIÓxcŢĞð†´E®žÚPt†ĴXØx¶˜@«ŕŕQGƒ‹Yfa[şu“ßǩ™đš_X³ijÕčC]kbc•¥CS¯ëÍB©÷‹–³­Siˆ_}m˜YTtž³xlàcȂzÀD}ÂOQ³ÐTĨ¯†ƗòËŖ[hœł‹Ŧv~††}ÂZž«¤lPǕ£ªÝŴÅR§ØnhcŒtâk‡nύ­ľŹUÓÝdKuķ‡I§oTũÙďkęĆH¸ÓŒ\\ăŒ¿PcnS{wBIvɘĽ[GqµuŸŇôYgûƒZcaŽ©@½Õǽys¯}lgg@­C\\£as€IdÍuCQñ[L±ęk·‹ţb¨©kK—’»›KC²‘òGKmĨS`ƒ˜UQ™nk}AGē”sqaJ¥ĐGR‰ĎpCuÌy ã iMc”plk|tRk†ðœev~^‘´†¦ÜŽSí¿_iyjI|ȑ|¿_»d}qŸ^{“Ƈdă}Ÿtqµ`Ƴĕg}V¡om½fa™Ço³TTj¥„tĠ—Ry”K{ùÓjuµ{t}uËR‘iŸvGŠçJFjµŠÍyqΘàQÂFewixGw½Yŷpµú³XU›½ġy™łå‰kÚwZXˆ·l„¢Á¢K”zO„Λ΀jc¼htoDHr…|­J“½}JZ_¯iPq{tę½ĕ¦Zpĵø«kQ…Ťƒ]MÛfaQpě±ǽ¾]u­Fu‹÷nƒ™čįADp}AjmcEǒaª³o³ÆÍSƇĈÙDIzˑ赟^ˆKLœ—i—Þñ€[œƒaA²zz‰Ì÷Dœ|[šíijgf‚ÕÞd®|`ƒĆ~„oĠƑô³Ŋ‘D×°¯CsŠøÀ«ì‰UMhTº¨¸ǡîS–Ô„DruÂÇZ•ÖEŽ’vPZ„žW”~؋ÐtĄE¢¦Ðy¸bŠô´oŬ¬Ž²Ês~€€]®tªašpŎJ¨Öº„_ŠŔ–`’Ŗ^Ѝ\\Ĝu–”~m²Ƹ›¸fW‰ĦrƔ}Î^gjdfÔ¡J}\\n C˜¦þWxªJRÔŠu¬ĨĨmF†dM{\\d\\ŠYÊ¢ú@@¦ª²SŠÜsC–}fNècbpRmlØ^g„d¢aÒ¢CZˆZxvÆ¶N¿’¢T@€uCœ¬^ĊðÄn|žlGl’™Rjsp¢ED}€Fio~ÔNŽ‹„~zkĘHVsDzßjƒŬŒŠŢ`Pûàl¢˜\\ÀœEhŽİgÞē X¼Pk–„|m"
],
"encodeOffsets": [[118256, 37017]]
}
},
{
"type": "Feature",
"id": "420000",
"properties": {
"id": "420000",
"cp": [113.298572, 30.684355],
"name": "湖北",
"childNum": 3
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@AB‚"],
["@@lskt"],
[
"@@¾«}{ra®pîÃ\\™›{øCŠËyyB±„b\\›ò˜Ý˜jK›‡L ]ĎĽÌ’JyÚCƈćÎT´Å´pb©È‘dFin~BCo°BĎĚømvŒ®E^vǾ½Ĝ²Ro‚bÜeNŽ„^ĺ£R†¬lĶ÷YoĖ¥Ě¾|sOr°jY`~I”¾®I†{GqpCgyl{‡£œÍƒÍyPL“¡ƒ¡¸kW‡xYlÙæŠšŁĢzœ¾žV´W¶ùŸo¾ZHxjwfx„GNÁ•³Xéæl¶‰EièIH‰ u’jÌQ~v|sv¶Ôi|ú¢Fh˜Qsğ¦ƒSiŠBg™ÐE^ÁÐ{–čnOÂȞUÎóĔ†ÊēIJ}Z³½Mŧïeyp·uk³DsѨŸL“¶_œÅuèw»—€¡WqÜ]\\‘Ò§tƗcÕ¸ÕFÏǝĉăxŻČƟO‡ƒKÉġÿ×wg”÷IÅzCg†]m«ªGeçÃTC’«[‰t§{loWeC@ps_Bp‘­r‘„f_``Z|ei¡—oċMqow€¹DƝӛDYpûs•–‹Ykıǃ}s¥ç³[§ŸcYЧHK„«Qy‰]¢“wwö€¸ïx¼ņ¾Xv®ÇÀµRĠЋžHMž±cÏd„ƒǍũȅȷ±DSyúĝ£ŤĀàtÖÿï[îb\\}pĭÉI±Ñy…¿³x¯N‰o‰|¹H™ÏÛm‹júË~Tš•u˜ęjCöAwě¬R’đl¯ Ñb­‰ŇT†Ŀ_[Œ‘IčĄʿnM¦ğ\\É[T·™k¹œ©oĕ@A¾w•ya¥Y\\¥Âaz¯ãÁ¡k¥ne£Ûw†E©Êō¶˓uoj_Uƒ¡cF¹­[Wv“P©w—huÕyBF“ƒ`R‹qJUw\\i¡{jŸŸEPïÿ½fć…QÑÀQ{ž‚°‡fLԁ~wXg—ītêݾ–ĺ‘Hdˆ³fJd]‹HJ²…E€ƒoU¥†HhwQsƐ»Xmg±çve›]Dm͂PˆoCc¾‹_h”–høYrŊU¶eD°Č_N~øĹĚ·`z’]Äþp¼…äÌQŒv\\rCŒé¾TnkžŐڀÜa‡“¼ÝƆ̶Ûo…d…ĔňТJq’Pb ¾|JŒ¾fXŠƐîĨ_Z¯À}úƲ‹N_ĒĊ^„‘ĈaŐyp»CÇĕKŠšñL³ŠġMŒ²wrIÒŭxjb[œžn«øœ˜—æˆàƒ ^²­h¯Ú€ŐªÞ¸€Y²ĒVø}Ā^İ™´‚LŠÚm„¥ÀJÞ{JVŒųÞŃx×sxxƈē ģMř–ÚðòIf–Ċ“Œ\\Ʈ±ŒdʧĘD†vČ_Àæ~DŒċ´A®µ†¨ØLV¦êHÒ¤"
]
],
"encodeOffsets": [[[113712, 34000]], [[115612, 30507]], [[113649, 34054]]]
}
},
{
"type": "Feature",
"id": "430000",
"properties": { "id": "430000", "cp": [111.782279, 28.09409], "name": "湖南", "childNum": 3 },
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@—n„FTs"],
["@@ßÅÆá‰½ÔXr—†CO™“…ËR‘ïÿĩ­TooQyšÓ[‹ŅBE¬–ÎÓXa„į§Ã¸G °ITxp‰úxÚij¥Ïš–̾ŠedžÄ©ĸG…œàGh‚€M¤–Â_U}Ċ}¢pczfŠþg¤€”ÇòAV‘‹M"],
[
"@@©K—ƒA·³CQ±Á«³BUŠƑ¹AŠtćOw™D]ŒJiØSm¯b£‘ylƒ›X…HËѱH•«–‘C^õľA–Å§¤É¥„ïyuǙuA¢^{ÌC´­¦ŷJ£^[†“ª¿‡ĕ~•Ƈ…•N… skóā‡¹¿€ï]ă~÷O§­@—Vm¡‹Qđ¦¢Ĥ{ºjԏŽŒª¥nf´•~ÕoŸž×Ûą‹MąıuZœmZcÒ IJβSÊDŽŶ¨ƚƒ’CÖŎªQؼrŭŽ­«}NÏürʬŒmjr€@ĘrTW ­SsdHzƓ^ÇÂyUi¯DÅYlŹu{hTœ}mĉ–¹¥ě‰Dÿë©ıÓ[Oº£ž“¥ót€ł¹MՄžƪƒ`Pš…Di–ÛUоÅ‌ìˆU’ñB“È£ýhe‰dy¡oċ€`pfmjP~‚kZa…ZsÐd°wj§ƒ@€Ĵ®w~^‚kÀÅKvNmX\\¨a“”сqvíó¿F„¤¡@ũÑVw}S@j}¾«pĂr–ªg àÀ²NJ¶¶Dô…K‚|^ª†Ž°LX¾ŴäPᜣEXd›”^¶›IJÞܓ~‘u¸ǔ˜Ž›MRhsR…e†`ÄofIÔ\\Ø  i”ćymnú¨cj ¢»–GČìƊÿШXeĈ¾Oð Fi ¢|[jVxrIQŒ„_E”zAN¦zLU`œcªx”OTu RLÄ¢dV„i`p˔vŎµªÉžF~ƒØ€d¢ºgİàw¸Áb[¦Zb¦–z½xBĖ@ªpº›šlS¸Ö\\Ĕ[N¥ˀmĎă’J\\‹ŀ`€…ňSڊĖÁĐiO“Ĝ«BxDõĚiv—ž–S™Ì}iùŒžÜnšÐºGŠ{Šp°M´w†ÀÒzJ²ò¨ oTçüöoÛÿñŽőФ‚ùTz²CȆȸǎۃƑÐc°dPÎŸğ˶[Ƚu¯½WM¡­Éž“’B·rížnZŸÒ `‡¨GA¾\\pē˜XhÆRC­üWGġu…T靧Ŏѝ©ò³I±³}_‘‹EÃħg®ęisÁPDmÅ{‰b[Rşs·€kPŸŽƥƒóRo”O‹ŸVŸ~]{g\\“êYƪ¦kÝbiċƵŠGZ»Ěõ…ó·³vŝž£ø@pyö_‹ëŽIkѵ‡bcѧy…×dY؎ªiþž¨ƒ[]f]Ņ©C}ÁN‡»hĻħƏ’ĩ"
]
],
"encodeOffsets": [[[115640, 30489]], [[112543, 27312]], [[116690, 26230]]]
}
},
{
"type": "Feature",
"id": "440000",
"properties": {
"id": "440000",
"cp": [113.280637, 23.125178],
"name": "广东",
"childNum": 24
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@QdˆAua"],
["@@ƒlxDLo"],
["@@sbhNLo"],
["@@Ă āŸ"],
["@@WltO[["],
["@@Krœ]S"],
["@@e„„I]y"],
["@@I|„Mym"],
["@@ƒÛ³LSŒž¼Y"],
["@@nvºB–ëui©`¾"],
["@@zdšÛ›Jw®"],
["@@†°…¯"],
["@@a yAª¸ËJIx،@€ĀHAmßV¡o•fu•o"],
["@@šs‰ŗÃÔėAƁ›ZšÄ ~°ČP‚‹äh"],
["@@‹¶Ý’Ì‚vmĞh­ı‡Q"],
["@@HœŠdSjĒ¢D}war…“u«ZqadYM"],
["@@elŒ\\LqqU"],
["@@~rMo\\"],
["@@f„^ƒC"],
["@@øPªoj÷ÍÝħXČx”°Q¨ıXNv"],
["@@gÇƳˆŽˆ”oˆŠˆ[~tly"],
["@@E–ÆC¿‘"],
["@@OŽP"],
[
"@@w‹†đóg‰™ĝ—[³‹¡VÙæÅöM̳¹pÁaËýý©D©Ü“JŹƕģGą¤{Ùū…ǘO²«BƱéA—Ò‰ĥ‡¡«BhlmtÃPµyU¯uc“d·w_bŝcīímGOŽ|KP’ȏ‡ŹãŝIŕŭŕ@Óoo¿ē‹±ß}Ž…ŭ‚ŸIJWÈCőâUâǙI›ğʼn©I›ijEׅÁ”³Aó›wXJþ±ÌŒÜӔĨ£L]ĈÙƺZǾĆĖMĸĤfŒÎĵl•ŨnȈ‘ĐtF”Š–FĤ–‚êk¶œ^k°f¶gŠŽœ}®Fa˜f`vXŲxl˜„¦–ÔÁ²¬ÐŸ¦pqÊ̲ˆi€XŸØRDÎ}†Ä@ZĠ’s„x®AR~®ETtĄZ†–ƈfŠŠHâÒÐA†µ\\S¸„^wĖkRzŠalŽŜ|E¨ÈNĀňZTŒ’pBh£\\ŒĎƀuXĖtKL–¶G|Ž»ĺEļĞ~ÜĢÛĊrˆO˜Ùîvd]nˆ¬VœÊĜ°R֟pM††–‚ƂªFbwžEÀˆ˜©Œž\\…¤]ŸI®¥D³|ˎ]CöAŤ¦…æ’´¥¸Lv¼€•¢ĽBaô–F~—š®²GÌҐEY„„œzk¤’°ahlV՞I^‹šCxĈPŽsB‰ƒºV‰¸@¾ªR²ĨN]´_eavSi‡vc•}p}Đ¼ƌkJœÚe thœ†_¸ ºx±ò_xN›Ë‹²‘@ƒă¡ßH©Ùñ}wkNÕ¹ÇO½¿£ĕ]ly_WìIžÇª`ŠuTÅxYĒÖ¼k֞’µ‚MžjJÚwn\\h‘œĒv]îh|’È›Ƅøègž¸Ķß ĉĈWb¹ƀdéƌNTtP[ŠöSvrCZžžaGuœbo´ŖÒÇА~¡zCI…özx¢„Pn‹•‰Èñ @ŒĥÒ¦†]ƞŠV}³ăĔñiiÄÓVépKG½Ä‘ÓávYo–C·sit‹iaÀy„ŧΡÈYDÑům}‰ý|m[węõĉZÅxUO}÷N¹³ĉo_qtă“qwµŁYلǝŕ¹tïÛUïmRCº…ˆĭ|µ›ÕÊK™½R‘ē ó]‘–GªęAx–»HO£|ām‡¡diď×YïYWªʼnOeÚtĐ«zđ¹T…ā‡úE™á²\\‹ķÍ}jYàÙÆſ¿Çdğ·ùTßÇţʄ¡XgWÀLJğ·¿ÃˆOj YÇ÷Qě‹i"
]
],
"encodeOffsets": [
[[117381, 22988]],
[[116552, 22934]],
[[116790, 22617]],
[[116973, 22545]],
[[116444, 22536]],
[[116931, 22515]],
[[116496, 22490]],
[[116453, 22449]],
[[113301, 21439]],
[[118726, 21604]],
[[118709, 21486]],
[[113210, 20816]],
[[115482, 22082]],
[[113171, 21585]],
[[113199, 21590]],
[[115232, 22102]],
[[115739, 22373]],
[[115134, 22184]],
[[113056, 21175]],
[[119573, 21271]],
[[119957, 24020]],
[[115859, 22356]],
[[116561, 22649]],
[[116285, 22746]]
]
}
},
{
"type": "Feature",
"id": "450000",
"properties": { "id": "450000", "cp": [108.320004, 22.82402], "name": "广西", "childNum": 2 },
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@H– TQ§•A"],
[
"@@ĨʪƒLƒƊDÎĹĐCǦė¸zÚGn£¾›rªŀÜt¬@֛ڈSx~øOŒ˜ŶÐÂæȠ\\„ÈÜObĖw^oބLf¬°bI lTØB̈F£Ć¹gñĤaY“t¿¤VSñœK¸¤nM†¼‚JE±„½¸šŠño‹ÜCƆæĪ^ŠĚQÖ¦^‡ˆˆf´Q†üÜʝz¯šlzUĺš@쇀p¶n]sxtx¶@„~ÒĂJb©gk‚{°‚~c°`ԙ¬rV\\“la¼¤ôá`¯¹LC†ÆbŒxEræO‚v[H­˜„[~|aB£ÖsºdAĐzNÂðsŽÞƔ…Ĥªbƒ–ab`ho¡³F«èVloޤ™ÔRzpp®SŽĪº¨ÖƒºN…ij„d`’a”¦¤F³ºDÎńĀìŠCžĜº¦Ċ•~nS›|gźvZkCÆj°zVÈÁƔ]LÊFZg…čP­kini«‹qǀcz͔Y®¬Ů»qR×ō©DՄ‘§ƙǃŵTÉĩ±ŸıdÑnYY›IJvNĆÌØÜ Öp–}e³¦m‹©iÓ|¹Ÿħņ›|ª¦QF¢Â¬ʖovg¿em‡^ucà÷gՎuŒíÙćĝ}FϼĹ{µHK•sLSđƃr‹č¤[Ag‘oS‹ŇYMÿ§Ç{Fśbky‰lQxĕƒ]T·¶[B…ÑÏGáşşƇe€…•ăYSs­FQ}­Bƒw‘tYğÃ@~…C̀Q ×W‡j˱rÉ¥oÏ ±«ÓÂ¥•ƒ€k—ŽwWűŒmcih³K›~‰µh¯e]lµ›él•E쉕E“ďs‡’mǖŧē`ãògK_ÛsUʝ“ćğ¶hŒöŒO¤Ǜn³Žc‘`¡y‹¦C‘ez€YŠwa™–‘[ďĵűMę§]X˜Î_‚훘Û]é’ÛUćİÕBƣ±…dƒy¹T^džûÅÑŦ·‡PĻþÙ`K€¦˜…¢ÍeœĥR¿Œ³£[~Œäu¼dl‰t‚†W¸oRM¢ď\\zœ}Æzdvň–{ÎXF¶°Â_„ÒÂÏL©Ö•TmuŸ¼ãl‰›īkiqéfA„·Êµ\\őDc¥ÝF“y›Ôć˜c€űH_hL܋êĺШc}rn`½„Ì@¸¶ªVLŒŠhŒ‹\\•Ţĺk~ŽĠið°|gŒtTĭĸ^x‘vK˜VGréAé‘bUu›MJ‰VÃO¡…qĂXËS‰ģãlýàŸ_ju‡YÛÒB†œG^˜é֊¶§ŽƒEG”ÅzěƒƯ¤Ek‡N[kdåucé¬dnYpAyČ{`]þ¯T’bÜÈk‚¡Ġ•vŒàh„ÂƄ¢Jî¶²"
]
],
"encodeOffsets": [[[111707, 21520]], [[107619, 25527]]]
}
},
{
"type": "Feature",
"id": "460000",
"properties": { "id": "460000", "cp": [109.83119, 19.031971], "name": "海南", "childNum": 1 },
"geometry": {
"type": "Polygon",
"coordinates": ["@@š¦Ŝil¢”XƦ‘ƞò–ïè§ŞCêɕrŧůÇąĻõ™·ĉ³œ̅kÇm@ċȧƒŧĥ‰Ľʉ­ƅſ“ȓÒ˦ŝE}ºƑ[ÍĜȋ gÎfǐÏĤ¨êƺ\\Ɔ¸ĠĎvʄȀœÐ¾jNðĀÒRŒšZdž™zÐŘΰH¨Ƣb²_Ġ "],
"encodeOffsets": [[112750, 20508]]
}
},
{
"type": "Feature",
"id": "510000",
"properties": {
"id": "510000",
"cp": [104.065735, 30.659462],
"name": "四川",
"childNum": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@LqKr"],
[
"@@Š[ĻéV£ž_ţġñpG •réÏ·~ąSfy×͂·ºſƽiÍıƣıĻmHH}siaX@iǰÁÃ×t«ƒ­Tƒ¤J–JJŒyJ•ÈŠ`Ohߦ¡uËhIyCjmÿw…ZG……Ti‹SˆsO‰žB²ŸfNmsPaˆ{M{ŠõE‘^Hj}gYpaeuž¯‘oáwHjÁ½M¡pM“–uå‡mni{fk”\\oƒÎqCw†EZ¼K›ĝŠƒAy{m÷L‡wO×SimRI¯rK™õBS«sFe‡]fµ¢óY_ÆPRcue°Cbo׌bd£ŌIHgtrnyPt¦foaXďx›lBowz‹_{ÊéWiêE„GhܸºuFĈIxf®Ž•Y½ĀǙ]¤EyŸF²ċ’w¸¿@g¢§RGv»–áŸW`ÃĵJwi]t¥wO­½a[׈]`Ãi­üL€¦LabbTÀå’c}Íh™Æhˆ‹®BH€î|Ék­¤S†y£„ia©taį·Ɖ`ō¥Uh“O…ƒĝLk}©Fos‰´›Jm„µlŁu—…ø–nÑJWΪ–YÀïAetTžŅ‚ӍG™Ë«bo‰{ıwodƟ½ƒžOġܑµxàNÖ¾P²§HKv¾–]|•B‡ÆåoZ`¡Ø`ÀmºĠ~ÌЧnDž¿¤]wğ@sƒ‰rğu‰~‘Io”[é±¹ ¿žſđӉ@q‹gˆ¹zƱřaí°KtǤV»Ã[ĩǭƑ^ÇÓ@ỗs›Zϕ‹œÅĭ€Ƌ•ěpwDóÖሯneQˌq·•GCœýS]xŸ·ý‹q³•O՜Œ¶Qzßti{ř‰áÍÇWŝŭñzÇW‹pç¿JŒ™‚Xœĩè½cŒF–ÂLiVjx}\\N†ŇĖ¥Ge–“JA¼ÄHfÈu~¸Æ«dE³ÉMA|b˜Ò…˜ćhG¬CM‚õŠ„ƤąAvƒüV€éŀ‰_V̳ĐwQj´·ZeÈÁ¨X´Æ¡Qu·»Ÿ“˜ÕZ³ġqDo‰y`L¬gdp°şŠp¦ėìÅĮZްIä”h‚‘ˆzŠĵœf²å ›ĚрKp‹IN|‹„Ñz]ń……·FU×é»R³™MƒÉ»GM«€ki€™ér™}Ã`¹ăÞmȝnÁîRǀ³ĜoİzŔwǶVÚ£À]ɜ»ĆlƂ²Ġ…þTº·àUȞÏʦ¶†I’«dĽĢdĬ¿–»Ĕ׊h\\c¬†ä²GêëĤł¥ÀǿżÃÆMº}BÕĢyFVvw–ˆxBèĻĒ©Ĉ“tCĢɽŠȣ¦āæ·HĽî“ôNԓ~^¤Ɗœu„œ^s¼{TA¼ø°¢İªDè¾Ň¶ÝJ‘®Z´ğ~Sn|ªWÚ©òzPOȸ‚bð¢|‹øĞŠŒœŒQìÛÐ@Ğ™ǎRS¤Á§d…i“´ezÝúØã]Hq„kIŸþËQǦÃsǤ[E¬ÉŪÍxXƒ·ÖƁİlƞ¹ª¹|XÊwn‘ÆƄmÀêErĒtD®ċæcQƒ”E®³^ĭ¥©l}äQto˜ŖÜqƎkµ–„ªÔĻĴ¡@Ċ°B²Èw^^RsºT£ڿœQP‘JvÄz„^Đ¹Æ¯fLà´GC²‘dt˜­ĀRt¼¤ĦOðğfÔðDŨŁĞƘïžPȆ®âbMüÀXZ ¸£@Ś›»»QÉ­™]d“sÖ×_͖_ÌêŮPrĔĐÕGĂeZÜîĘqBhtO ¤tE[h|Y‹Ô‚ZśÎs´xº±UŒ’ñˆt|O’ĩĠºNbgþŠJy^dÂY Į„]Řz¦gC‚³€R`Šz’¢AjŒ¸CL„¤RÆ»@­Ŏk\\Ç´£YW}z@Z}‰Ã¶“oû¶]´^N‡Ò}èN‚ª–P˜Íy¹`S°´†ATe€VamdUĐwʄvĮÕ\\ƒu‹Æŗ¨Yp¹àZÂm™Wh{á„}WØǍ•Éüw™ga§áCNęÎ[ĀÕĪgÖɪX˜øx¬½Ů¦¦[€—„NΆL€ÜUÖ´òrÙŠxR^–†J˜k„ijnDX{Uƒ~ET{ļº¦PZc”jF²Ė@Žp˜g€ˆ¨“B{ƒu¨ŦyhoÚD®¯¢˜ WòàFΤ¨GDäz¦kŮPœġq˚¥À]€Ÿ˜eŽâÚ´ªKxī„Pˆ—Ö|æ[xäJÞĥ‚s’NÖ½ž€I†¬nĨY´®Ð—ƐŠ€mD™ŝuäđđEb…e’e_™v¡}ìęNJē}q”É埁T¯µRs¡M@}ůa†a­¯wvƉåZwž\\Z{åû^›"
]
],
"encodeOffsets": [[[108815, 30935]], [[110617, 31811]]]
}
},
{
"type": "Feature",
"id": "520000",
"properties": {
"id": "520000",
"cp": [106.713478, 26.578343],
"name": "贵州",
"childNum": 3
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@†G\\†lY£‘in"],
["@@q‚|ˆ‚mc¯tχVSÎ"],
[
"@@hÑ£Is‡NgßH†›HªķÃh_¹ƒ¡ĝħń¦uيùŽgS¯JHŸ|sÝÅtÁïyMDč»eÕtA¤{b\\}—ƒG®u\\åPFq‹wÅaD…žK°ºâ_£ùbµ”mÁ‹ÛœĹM[q|hlaªāI}тƒµ@swtwm^oµˆD鼊yV™ky°ÉžûÛR…³‚‡eˆ‡¥]RՋěħ[ƅåÛDpŒ”J„iV™™‰ÂF²I…»mN·£›LbÒYb—WsÀbŽ™pki™TZĄă¶HŒq`……ĥ_JŸ¯ae«ƒKpÝx]aĕÛPƒÇȟ[ÁåŵÏő—÷Pw}‡TœÙ@Õs«ĿÛq©½œm¤ÙH·yǥĘĉBµĨÕnđ]K„©„œá‹ŸG纍§Õßg‡ǗĦTèƤƺ{¶ÉHÎd¾ŚÊ·OÐjXWrãLyzÉAL¾ę¢bĶėy_qMĔąro¼hĊžw¶øV¤w”²Ĉ]ʚKx|`ź¦ÂÈdr„cȁbe¸›`I¼čTF´¼Óýȃr¹ÍJ©k_șl³´_pН`oÒh޶pa‚^ÓĔ}D»^Xyœ`d˜[Kv…JPhèhCrĂĚÂ^Êƌ wˆZL­Ġ£šÁbrzOIl’MM”ĪŐžËr×ÎeŦŽtw|Œ¢mKjSǘňĂStÎŦEtqFT†¾†E쬬ôxÌO¢Ÿ KгŀºäY†„”PVgŎ¦Ŋm޼VZwVlŒ„z¤…ž£Tl®ctĽÚó{G­A‡ŒÇgeš~Αd¿æaSba¥KKûj®_ć^\\ؾbP®¦x^sxjĶI_Ä X‚⼕Hu¨Qh¡À@Ëô}ޱžGNìĎlT¸ˆ…`V~R°tbÕĊ`¸úÛtπFDu€[ƒMfqGH·¥yA‰ztMFe|R‚_Gk†ChZeÚ°to˜v`x‹b„ŒDnÐ{E}šZ˜è€x—†NEފREn˜[Pv@{~rĆAB§‚EO¿|UZ~ì„Uf¨J²ĂÝÆ€‚sª–B`„s¶œfvö¦ŠÕ~dÔq¨¸º»uù[[§´sb¤¢zþFœ¢Æ…Àhˆ™ÂˆW\\ıŽËI݊o±ĭŠ£þˆÊs}¡R]ŒěƒD‚g´VG¢‚j±®è†ºÃmpU[Á›‘Œëº°r›ÜbNu¸}Žº¼‡`ni”ºÔXĄ¤¼Ôdaµ€Á_À…†ftQQgœR—‘·Ǔ’v”}Ýלĵ]µœ“Wc¤F²›OĩųãW½¯K‚©…]€{†LóµCIµ±Mß¿hŸ•©āq¬o‚½ž~@i~TUxŪÒ¢@ƒ£ÀEîôruń‚”“‚b[§nWuMÆLl¿]x}ij­€½"
]
],
"encodeOffsets": [[[112158, 27383]], [[112105, 27474]], [[112095, 27476]]]
}
},
{
"type": "Feature",
"id": "530000",
"properties": {
"id": "530000",
"cp": [101.512251, 24.740609],
"name": "云南",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@[„ùx½}ÑRH‘YīĺûsÍn‘iEoã½Ya²ė{c¬ĝg•ĂsA•ØÅwď‚õzFjw}—«Dx¿}UũlŸê™@•HÅ­F‰¨ÇoJ´Ónũuą¡Ã¢pÒŌ“Ø TF²‚xa²ËX€‚cʋlHîAßËŁkŻƑŷÉ©h™W­æßU‡“Ës¡¦}•teèÆ¶StǀÇ}Fd£j‹ĈZĆÆ‹¤T‚č\\Dƒ}O÷š£Uˆ§~ŃG™‚åŃDĝ¸œTsd¶¶Bªš¤u¢ŌĎo~t¾ÍŶÒtD¦Ú„iôö‰€z›ØX²ghįh½Û±¯€ÿm·zR¦Ɵ`ªŊÃh¢rOԍ´£Ym¼èêf¯ŪĽn„†cÚbŒw\\zlvWžªâˆ ¦g–mĿBş£¢ƹřbĥkǫßeeZkÙIKueT»sVesb‘aĕ  ¶®dNœĄÄpªyސ¼—„³BE˜®l‡ŽGœŭCœǶwêżĔÂe„pÍÀQƞpC„–¼ŲÈ­AÎô¶R„ä’Q^Øu¬°š_Èôc´¹ò¨P΢hlϦ´Ħ“Æ´sâDŽŲPnÊD^¯°’Upv†}®BP̪–jǬx–Söwlfòªv€qĸ|`H€­viļ€ndĜ­Ćhň•‚em·FyށqóžSᝑ³X_ĞçêtryvL¤§z„¦c¦¥jnŞk˜ˆlD¤øz½ĜàžĂŧMÅ|áƆàÊcðÂF܎‚áŢ¥\\\\º™İøÒÐJĴ‡„îD¦zK²ǏÎEh~’CD­hMn^ÌöÄ©ČZÀžaü„fɭyœpį´ěFűk]Ôě¢qlÅĆÙa¶~Äqššê€ljN¬¼H„ÊšNQ´ê¼VظE††^ŃÒyŒƒM{ŒJLoÒœęæŸe±Ķ›y‰’‡gã“¯JYÆĭĘëo¥Š‰o¯hcK«z_pŠrC´ĢÖY”—¼ v¸¢RŽÅW³Â§fǸYi³xR´ďUˊ`êĿU„û€uĆBƒƣö‰N€DH«Ĉg†——Ñ‚aB{ÊNF´¬c·Åv}eÇÃGB»”If•¦HňĕM…~[iwjUÁKE•Ž‹¾dĪçW›šI‹èÀŒoÈXòyŞŮÈXâÎŚŠj|àsRy‹µÖ›–Pr´þŒ ¸^wþTDŔ–Hr¸‹žRÌmf‡żÕâCôox–ĜƌÆĮŒ›Ð–œY˜tâŦÔ@]ÈǮƒ\\μģUsȯLbîƲŚºyh‡rŒŠ@ĒԝƀŸÀ²º\\êp“’JŠ}ĠvŠqt„Ġ@^xÀ£È†¨mËÏğ}n¹_¿¢×Y_æpˆÅ–A^{½•Lu¨GO±Õ½ßM¶w’ÁĢۂP‚›Ƣ¼pcIJxŠ|ap̬HšÐŒŊSfsðBZ¿©“XÏÒK•k†÷Eû¿‰S…rEFsÕūk”óVǥʼniTL‚¡n{‹uxţÏh™ôŝ¬ğōN“‘NJkyPaq™Âğ¤K®‡YŸxÉƋÁ]āęDqçgOg†ILu—\\_gz—]W¼ž~CÔē]bµogpў_oď`´³Țkl`IªºÎȄqÔþž»E³ĎSJ»œ_f·‚adÇqƒÇc¥Á_Źw{™L^ɱćx“U£µ÷xgĉp»ĆqNē`rĘzaĵĚ¡K½ÊBzyäKXqiWPÏɸ½řÍcÊG|µƕƣG˛÷Ÿk°_^ý|_zċBZocmø¯hhcæ\\lˆMFlư£Ĝ„ÆyH“„F¨‰µêÕ]—›HA…àӄ^it `þßäkŠĤÎT~Wlÿ¨„ÔPzUC–NVv [jâôDôď[}ž‰z¿–msSh‹¯{jïğl}šĹ[–őŒ‰gK‹©U·µË@¾ƒm_~q¡f¹…ÅË^»‘f³ø}Q•„¡Ö˳gͱ^ǁ…\\ëÃA_—¿bW›Ï[¶ƛ鏝£F{īZgm@|kHǭƁć¦UĔťƒ×ë}ǝƒeďºȡȘÏíBə£āĘPªij¶“ʼnÿ‡y©n‰ď£G¹¡I›Š±LÉĺÑdĉ܇W¥˜‰}g˜Á†{aqÃ¥aŠıęÏZ—ï`"
],
"encodeOffsets": [[104636, 22969]]
}
},
{
"type": "Feature",
"id": "540000",
"properties": { "id": "540000", "cp": [89.132212, 30.860361], "name": "西藏", "childNum": 1 },
"geometry": {
"type": "Polygon",
"coordinates": [
"@@hžľxŽŖ‰xƒÒVކºÅâAĪÝȆµę¯Ňa±r_w~uSÕň‘qOj]ɄQ…£Z……UDûoY’»©M[‹L¼qãË{V͕çWViŽ]ë©Ä÷àyƛh›ÚU°ŒŒa”d„cQƒ~Mx¥™cc¡ÙaSyF—ցk­ŒuRýq¿Ôµ•QĽ³aG{¿FµëªéĜÿª@¬·–K‰·àariĕĀ«V»Ŷ™Ĵū˜gèLǴŇƶaf‹tŒèBŚ£^Šâ†ǐÝ®–šM¦ÁǞÿ¬LhŸŽJ¾óƾƺcxw‹f]Y…´ƒ¦|œQLn°aœdĊ…œ\\¨o’œǀÍŎœ´ĩĀd`tÊQŞŕ|‚¨C^©œĈ¦„¦ÎJĊ{ŽëĎjª²rЉšl`¼Ą[t|¦St辉PŒÜK¸€d˜Ƅı]s¤—î_v¹ÎVòŦj˜£Əsc—¬_Ğ´|٘¦Avަw`ăaÝaa­¢e¤ı²©ªSªšÈMĄwžÉØŔì@T‘¤—Ę™\\õª@”þo´­xA s”ÂtŎKzó´ÇĊµ¢rž^nĊ­Æ¬×üGž¢‚³ {âĊ]š™G‚~bÀgVjzlhǶf€žOšfdЉªB]pj„•TO–tĊ‚n¤}®¦ƒČ¥d¢¼»ddš”Y¼Žt—¢eȤJ¤}Ǿ¡°§¤AГlc@ĝ”sªćļđAç‡wx•UuzEÖġ~AN¹ÄÅȀݦ¿ģŁéì±H…ãd«g[؉¼ēÀ•cīľġ¬cJ‘µ…ÐʥVȝ¸ßS¹†ý±ğkƁ¼ą^ɛ¤Ûÿ‰b[}¬ōõÃ]ËNm®g@•Bg}ÍF±ǐyL¥íCˆƒIij€Ï÷њį[¹¦[⚍EÛïÁÉdƅß{âNÆāŨߝ¾ě÷yC£‡k­´ÓH@¹†TZ¥¢įƒ·ÌAЧ®—Zc…v½ŸZ­¹|ŕWZqgW“|ieZÅYVӁqdq•bc²R@†c‡¥Rã»Ge†ŸeƃīQ•}J[ғK…¬Ə|o’ėjġĠÑN¡ð¯EBčnwôɍėªƒ²•CλŹġǝʅįĭạ̃ūȹ]ΓͧgšsgȽóϧµǛ†ęgſ¶ҍć`ĘąŌJޚä¤rÅň¥ÖÁUětęuůÞiĊÄÀ\\Æs¦ÓRb|Â^řÌkÄŷ¶½÷‡f±iMݑ›‰@ĥ°G¬ÃM¥n£Øą‚ğ¯ß”§aëbéüÑOčœk£{\\‘eµª×M‘šÉfm«Ƒ{Å׃Gŏǩãy³©WÑăû‚··‘Q—òı}¯ã‰I•éÕÂZ¨īès¶ZÈsŽæĔTŘvŽgÌsN@îá¾ó@‰˜ÙwU±ÉT廣TđŸWxq¹Zo‘b‹s[׌¯cĩv‡Œėŧ³BM|¹k‰ªħ—¥TzNYnݍßpęrñĠĉRS~½ŠěVVе‚õ‡«ŒM££µB•ĉ¥áºae~³AuĐh`Ü³ç@BۘïĿa©|z²Ý¼D”£à貋ŸƒIƒû›I ā€óK¥}rÝ_Á´éMaň¨€~ªSĈ½Ž½KÙóĿeƃÆBŽ·¬ën×W|Uº}LJrƳ˜lŒµ`bÔ`QˆˆÐÓ@s¬ñIŒÍ@ûws¡åQÑßÁ`ŋĴ{Ī“T•ÚÅTSij‚‹Yo|Ç[ǾµMW¢ĭiÕØ¿@˜šMh…pÕ]j†éò¿OƇĆƇp€êĉâlØw–ěsˆǩ‚ĵ¸c…bU¹ř¨WavquSMzeo_^gsÏ·¥Ó@~¯¿RiīB™Š\\”qTGªÇĜçPoŠÿfñòą¦óQīÈáP•œābß{ƒZŗĸIæÅ„hnszÁCËìñšÏ·ąĚÝUm®ó­L·ăU›Èíoù´Êj°ŁŤ_uµ^‘°Œìǖ@tĶĒ¡Æ‡M³Ģ«˜İĨÅ®ğ†RŽāð“ggheÆ¢z‚Ê©Ô\\°ÝĎz~ź¤Pn–MĪÖB£Ÿk™n鄧żćŠ˜ĆK„ǰ¼L¶è‰âz¨u¦¥LDĘz¬ýÎmĘd¾ß”Fz“hg²™Fy¦ĝ¤ċņbΛ@y‚Ąæm°NĮZRÖíŽJ²öLĸÒ¨Y®ƌÐV‰à˜tt_ڀÂyĠzž]Ţh€zĎ{†ĢX”ˆc|šÐqŽšfO¢¤ög‚ÌHNŽ„PKŖœŽ˜Uú´xx[xˆvĐCûŠìÖT¬¸^}Ìsòd´_އKgžLĴ…ÀBon|H@–Êx˜—¦BpŰˆŌ¿fµƌA¾zLjRxжF”œkĄźRzŀˆ~¶[”´Hnª–VƞuĒ­È¨ƎcƽÌm¸ÁÈM¦x͊ëÀxdžB’šú^´W†£–d„kɾĬpœw‚˂ØɦļĬIŚœÊ•n›Ŕa¸™~J°î”lɌxĤÊÈðhÌ®‚g˜T´øŽàCˆŽÀ^ªerrƘdž¢İP|Ė ŸWœªĦ^¶´ÂL„aT±üWƜ˜ǀRšŶUńšĖ[QhlLüA†‹Ü\\†qR›Ą©"
],
"encodeOffsets": [[90849, 37210]]
}
},
{
"type": "Feature",
"id": "610000",
"properties": {
"id": "610000",
"cp": [108.948024, 34.263161],
"name": "陕西",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@˜p¢—ȮµšûG™Ħ}Ħšðǚ¶òƄ€jɂz°{ºØkÈęâ¦jª‚Bg‚\\œċ°s¬Ž’]jžú ‚E”Ȍdž¬s„t‡”RˆÆdĠݎwܔ¸ôW¾ƮłÒ_{’Ìšû¼„jº¹¢GǪÒ¯ĘƒZ`ºŊƒecņąš~BÂgzpâēòYǠȰÌTΨÂWœ|fcŸă§uF—Œ@NŸ¢XLƒŠRMº[ğȣſï|¥J™kc`sʼnǷ’Y¹‹W@µ÷K…ãï³ÛIcñ·VȋڍÒķø©—þ¥ƒy‚ÓŸğęmWµÎumZyOŅƟĥÓ~sÑL¤µaŅY¦ocyZ{‰y c]{ŒTa©ƒ`U_Ěē£ωÊƍKù’K¶ȱÝƷ§{û»ÅÁȹÍéuij|¹cÑd‘ŠìUYƒŽO‘uF–ÕÈYvÁCqӃT•Ǣí§·S¹NgŠV¬ë÷Át‡°Dد’C´ʼnƒópģ}„ċcE˅FŸŸéGU¥×K…§­¶³B‹Č}C¿åċ`wġB·¤őcƭ²ő[Å^axwQO…ÿEËߌ•ĤNĔŸwƇˆÄŠńwĪ­Šo[„_KÓª³“ÙnK‰Çƒěœÿ]ď€ă_d©·©Ýŏ°Ù®g]±„Ÿ‡ß˜å›—¬÷m\\›iaǑkěX{¢|ZKlçhLt€Ňîŵ€œè[€É@ƉĄEœ‡tƇÏ˜³­ħZ«mJ…›×¾‘MtÝĦ£IwÄå\\Õ{‡˜ƒOwĬ©LÙ³ÙgBƕŀr̛ĢŭO¥lãyC§HÍ£ßEñŸX¡—­°ÙCgpťz‘ˆb`wI„vA|§”‡—hoĕ@E±“iYd¥OϹS|}F@¾oAO²{tfžÜ—¢Fǂ҈W²°BĤh^Wx{@„¬‚­F¸¡„ķn£P|ŸªĴ@^ĠĈæb–Ôc¶l˜Yi…–^Mi˜cϰÂ[ä€vï¶gv@À“Ĭ·lJ¸sn|¼u~a]’ÆÈtŌºJp’ƒþ£KKf~ЦUbyäIšĺãn‡Ô¿^­žŵMT–hĠܤko¼Ŏìąǜh`[tŒRd²IJ_œXPrɲ‰l‘‚XžiL§àƒ–¹ŽH˜°Ȧqº®QC—bA†„ŌJ¸ĕÚ³ĺ§ `d¨YjžiZvRĺ±öVKkjGȊĐePОZmļKÀ€‚[ŠŽ`ösìh†ïÎoĬdtKÞ{¬èÒÒBŒÔpIJÇĬJŊ¦±J«ˆY§‹@·pH€µàåVKe›pW†ftsAÅqC·¬ko«pHÆuK@oŸHĆۄķhx“e‘n›S³àǍrqƶRbzy€¸ËАl›¼EºpĤ¼Œx¼½~Ğ’”à@†ÚüdK^ˆmÌSj"
],
"encodeOffsets": [[110234, 38774]]
}
},
{
"type": "Feature",
"id": "620000",
"properties": {
"id": "620000",
"cp": [103.823557, 36.058039],
"name": "甘肃",
"childNum": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@VuUv"],
[
"@@ũ‹EĠtt~nkh`Q‰¦ÅÄÜdw˜Ab×ĠąJˆ¤DüègĺqBqœj°lI¡ĨÒ¤úSHbš‡ŠjΑBаaZˆ¢KJŽ’O[|A£žDx}Nì•HUnrk„ kp€¼Y kMJn[aG‚áÚÏ[½rc†}aQxOgsPMnUs‡nc‹Z…ž–sKúvA›t„Þġ’£®ĀYKdnFwš¢JE°”Latf`¼h¬we|€Æ‡šbj}GA€·~WŽ”—`†¢MC¤tL©IJ°qdf”O‚“bÞĬ¹ttu`^ZúE`Œ[@„Æsîz®¡’C„ƳƜG²“R‘¢R’m”fŽwĸg܃‚ą G@pzJM½mŠhVy¸uÈÔO±¨{LfæU¶ßGĂq\\ª¬‡²I‚¥IʼnÈīoı‹ÓÑAçÑ|«LÝcspīðÍg…të_õ‰\\ĉñLYnĝg’ŸRǡÁiHLlõUĹ²uQjYi§Z_c¨Ÿ´ĹĖÙ·ŋI…ƒaBD˜­R¹ȥr—¯G•ºß„K¨jWk’ɱŠOq›Wij\\a­‹Q\\sg_ĆǛōëp»£lğۀgS•ŶN®À]ˆÓäm™ĹãJaz¥V}‰Le¤L„ýo‘¹IsŋÅÇ^‘Žbz…³tmEÁ´aйcčecÇN•ĊãÁ\\蝗dNj•]j†—ZµkÓda•ćå]ğij@ ©O{¤ĸm¢ƒE·®ƒ«|@Xwg]A챝‡XǁÑdzªc›wQÚŝñsÕ³ÛV_ýƒ˜¥\\ů¥©¾÷w—Ž©WÕÊĩhÿÖÁRo¸V¬âDb¨šhûx–Ê×nj~Zâƒg|šXÁnßYoº§ZÅŘvŒ[„ĭÖʃuďxcVbnUSf…B¯³_Tzº—ΕO©çMÑ~Mˆ³]µ^püµ”ŠÄY~y@X~¤Z³€[Èōl@®Å¼£QKƒ·Di‹¡By‘ÿ‰Q_´D¥hŗyƒ^ŸĭÁZ]cIzý‰ah¹MĪğP‘s{ò‡‹‘²Vw¹t³Ŝˁ[ŽÑ}X\\gsFŸ£sPAgěp×ëfYHāďÖqēŭOÏë“dLü•\\iŒ”t^c®šRʺ¶—¢H°mˆ‘rYŸ£BŸ¹čIoľu¶uI]vģSQ{ƒUŻ”Å}QÂ|̋°ƅ¤ĩŪU ęĄžÌZҞ\\v˜²PĔ»ƢNHƒĂyAmƂwVmž`”]ȏb•”H`‰Ì¢²ILvĜ—H®¤Dlt_„¢JJÄämèÔDëþgºƫ™”aʎÌrêYi~ ÎݤNpÀA¾Ĕ¼b…ð÷’Žˆ‡®‚”üs”zMzÖĖQdȨý†v§Tè|ªH’þa¸|šÐ ƒwKĢx¦ivr^ÿ ¸l öæfƟĴ·PJv}n\\h¹¶v†·À|\\ƁĚN´Ĝ€çèÁz]ġ¤²¨QÒŨTIl‡ªťØ}¼˗ƦvÄùØE‹’«Fï˛Iq”ōŒTvāÜŏ‚íÛߜÛV—j³âwGăÂíNOŠˆŠPìyV³ʼnĖýZso§HіiYw[߆\\X¦¥c]ÔƩÜ·«j‡ÐqvÁ¦m^ċ±R™¦΋ƈťĚgÀ»IïĨʗƮްƝ˜ĻþÍAƉſ±tÍEÕÞāNU͗¡\\ſčåÒʻĘm ƭÌŹöʥ’ëQ¤µ­ÇcƕªoIýˆ‰Iɐ_mkl³ă‰Ɠ¦j—¡Yz•Ňi–}Msßõ–īʋ —}ƒÁVmŸ_[n}eı­Uĥ¼‘ª•I{ΧDӜƻėoj‘qYhĹT©oūĶ£]ďxĩ‹ǑMĝ‰q`B´ƃ˺Ч—ç~™²ņj@”¥@đ´ί}ĥtPńǾV¬ufӃÉC‹tÓ̻‰…¹£G³€]ƖƾŎĪŪĘ̖¨ʈĢƂlɘ۪üºňUðǜȢƢż̌ȦǼ‚ĤŊɲĖ­Kq´ï¦—ºĒDzņɾªǀÞĈĂD†½ĄĎÌŗĞrôñnŽœN¼â¾ʄľԆ|DŽŽ֦ज़ȗlj̘̭ɺƅêgV̍ʆĠ·ÌĊv|ýĖÕWĊǎÞ´õ¼cÒÒBĢ͢UĜð͒s¨ňƃLĉÕÝ@ɛƯ÷¿Ľ­ĹeȏijëCȚDŲyê×Ŗyò¯ļcÂßY…tÁƤyAã˾J@ǝrý‹‰@¤…rz¸oP¹ɐÚyᐇHŸĀ[Jw…cVeȴϜ»ÈŽĖ}ƒŰŐèȭǢόĀƪÈŶë;Ñ̆ȤМľĮEŔ—ĹŊũ~ËUă{ŸĻƹɁύȩþĽvĽƓÉ@ē„ĽɲßǐƫʾǗĒpäWÐxnsÀ^ƆwW©¦cÅ¡Ji§vúF¶Ž¨c~c¼īŒeXǚ‹\\đ¾JŽwÀďksãA‹fÕ¦L}wa‚o”Z’‹D½†Ml«]eÒÅaɲáo½FõÛ]ĻÒ¡wYR£¢rvÓ®y®LF‹LzĈ„ôe]gx}•|KK}xklL]c¦£fRtív¦†PĤoH{tK"
]
],
"encodeOffsets": [[[108619, 36299]], [[108589, 36341]]]
}
},
{
"type": "Feature",
"id": "630000",
"properties": { "id": "630000", "cp": [96.778916, 35.623178], "name": "青海", "childNum": 2 },
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@InJm"],
[
"@@CƒÆ½OŃĦsΰ~dz¦@@“Ņiš±è}ؘƄ˹A³r_ĞŠǒNΌĐw¤^ŬĵªpĺSZg’rpiƼĘԛ¨C|͖J’©Ħ»®VIJ~f\\m `Un„˜~ʌŸ•ĬàöNt•~ňjy–¢Zi˜Ɣ¥ĄŠk´nl`JʇŠJþ©pdƖ®È£¶ìRʦ‘źõƮËnŸʼėæÑƀĎ[‚˜¢VÎĂMÖÝÎF²sƊƀÎBļýƞ—¯ʘƭðħ¼Jh¿ŦęΌƇš¥²Q]Č¥nuÂÏriˆ¸¬ƪÛ^Ó¦d€¥[Wà…x\\ZŽjҕ¨GtpþYŊĕ´€zUO뇉P‰îMĄÁxH´á˜iÜUà›îÜՁĂÛSuŎ‹r“œJð̬EŒ‘FÁú×uÃÎkr“Ē{V}İ«O_ÌËĬ©ŽÓŧSRѱ§Ģ£^ÂyèçěM³Ƃę{[¸¿u…ºµ[gt£¸OƤĿéYŸõ·kŸq]juw¥Dĩƍ€õÇPéĽG‘ž©ã‡¤G…uȧþRcÕĕNy“yût“ˆ­‡ø‘†ï»a½ē¿BMoᣟÍj}éZËqbʍš“Ƭh¹ìÿÓAçãnIáI`ƒks£CG­ě˜Uy×Cy•…’Ÿ@¶ʡÊBnāzG„ơMē¼±O÷õJËĚăVŸĪũƆ£Œ¯{ËL½Ìzż“„VR|ĠTbuvJvµhĻĖH”Aëáa…­OÇðñęNw‡…œľ·L›mI±íĠĩPÉ×®ÿs—’cB³±JKßĊ«`…ađ»·QAmO’‘Vţéÿ¤¹SQt]]Çx€±¯A@ĉij¢Ó祖•ƒl¶ÅÛr—ŕspãRk~¦ª]Į­´“FR„åd­ČsCqđéFn¿Åƃm’Éx{W©ºƝºįkÕƂƑ¸wWūЩÈFž£\\tÈ¥ÄRÈýÌJ ƒlGr^×äùyÞ³fj”c†€¨£ÂZ|ǓMĝšÏ@ëÜőR‹›ĝ‰Œ÷¡{aïȷPu°ËXÙ{©TmĠ}Y³’­ÞIňµç½©C¡į÷¯B»|St»›]vƒųƒs»”}MÓ ÿʪƟǭA¡fs˜»PY¼c¡»¦c„ċ­¥£~msĉP•–Siƒ^o©A‰Šec‚™PeǵŽkg‚yUi¿h}aH™šĉ^|ᴟ¡HØûÅ«ĉ®]m€¡qĉ¶³ÈyôōLÁst“BŸ®wn±ă¥HSò뚣˜S’ë@לÊăxÇN©™©T±ª£IJ¡fb®ÞbŽb_Ą¥xu¥B—ž{łĝ³«`d˜Ɛt—¤ťiñžÍUuºí`£˜^tƃIJc—·ÛLO‹½Šsç¥Ts{ă\\_»™kϊ±q©čiìĉ|ÍIƒ¥ć¥›€]ª§D{ŝŖÉR_sÿc³Īō›ƿΑ›§p›[ĉ†›c¯bKm›R¥{³„Z†e^ŽŒwx¹dƽŽôIg §Mĕ ƹĴ¿—ǣÜ̓]‹Ý–]snåA{‹eŒƭ`ǻŊĿ\\ijŬű”YÂÿ¬jĖqŽßbЏ•L«¸©@ěĀ©ê¶ìÀEH|´bRľž–Ó¶rÀQþ‹vl®Õ‚E˜TzÜdb ˜hw¤{LR„ƒd“c‹b¯‹ÙVgœ‚ƜßzÃô쮍^jUèXΖ|UäÌ»rKŽ\\ŒªN‘¼pZCü†VY††¤ɃRi^rPҒTÖ}|br°qňb̰ªiƶGQ¾²„x¦PœmlŜ‘[Ĥ¡ΞsĦŸÔÏâ\\ªÚŒU\\f…¢N²§x|¤§„xĔsZPòʛ²SÐqF`ª„VƒÞŜĶƨVZŒÌL`ˆ¢dŐIqr\\oäõ–F礻Ŷ×h¹]Clـ\\¦ďÌį¬řtTӺƙgQÇÓHţĒ”´ÃbEÄlbʔC”|CˆŮˆk„Ʈ[ʼ¬ňœ´KŮÈΰÌζƶlð”ļA†TUvdTŠG†º̼ŠÔ€ŒsÊDԄveOg"
]
],
"encodeOffsets": [[[105308, 37219]], [[95370, 40081]]]
}
},
{
"type": "Feature",
"id": "640000",
"properties": { "id": "640000", "cp": [106.278179, 37.26637], "name": "宁夏", "childNum": 2 },
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
"@@KëÀęĞ«OęȿȕŸı]ʼn¡åįÕÔ«Ǵõƪ™ĚQÐZhv K°›öqÀѐS[ÃÖHƖčË‡nL]ûc…Ùß@‚“ĝ‘¾}w»»‹oģF¹œ»kÌÏ·{zPƒ§B­¢íyÅt@ƒ@áš]Yv_ssģ¼i߁”ĻL¾ġsKD£¡N_…“˜X¸}B~Haiˆ™Åf{«x»ge_bs“KF¯¡Ix™mELcÿZ¤­Ģ‘ƒÝœsuBLù•t†ŒYdˆmVtNmtOPhRw~bd…¾qÐ\\âÙH\\bImlNZŸ»loƒŸqlVm–Gā§~QCw¤™{A\\‘PKŸNY‡¯bF‡kC¥’sk‹Šs_Ã\\ă«¢ħkJi¯r›rAhĹûç£CU‡ĕĊ_ԗBixÅُĄnªÑaM~ħpOu¥sîeQ¥¤^dkKwlL~{L~–hw^‚ófćƒKyEŒ­K­zuÔ¡qQ¤xZÑ¢^ļöܾEpž±âbÊÑÆ^fk¬…NC¾‘Œ“YpxbK~¥Že֎ŒäBlt¿Đx½I[ĒǙŒWž‹f»Ĭ}d§dµùEuj¨‚IÆ¢¥dXªƅx¿]mtÏwßR͌X¢͎vÆzƂZò®ǢÌʆCrâºMÞzžÆMҔÊÓŊZľ–r°Î®Ȉmª²ĈUªĚøºˆĮ¦ÌĘk„^FłĬhĚiĀ˾iİbjÕ"
],
["@@mfwěwMrŢªv@G‰"]
],
"encodeOffsets": [[[109366, 40242]], [[108600, 36303]]]
}
},
{
"type": "Feature",
"id": "650000",
"properties": { "id": "650000", "cp": [85.617733, 40.792818], "name": "新疆", "childNum": 1 },
"geometry": {
"type": "Polygon",
"coordinates": [
"@@QØĔ²X¨”~ǘBºjʐߨvK”ƔX¨vĊOžÃƒ·¢i@~c—‡ĝe_«”Eš“}QxgɪëÏÃ@sÅyXoŖ{ô«ŸuX…ê•Îf`œC‚¹ÂÿÐGĮÕĞXŪōŸMźÈƺQèĽôe|¿ƸJR¤ĘEjcUóº¯Ĩ_ŘÁMª÷Ð¥Oéȇ¿ÖğǤǷÂF҇zÉx[]­Ĥĝ‰œ¦EP}ûƥé¿İƷTėƫœŕƅ™ƱB»Đ±’ēO…¦E–•}‘`cȺrĦáŖuҞª«IJ‡πdƺÏØZƴwʄ¤ĖGЙǂZ̓èH¶}ÚZצʥĪï|ÇĦMŔ»İĝLj‹ì¥Βœba­¯¥ǕǚkĆŵĦɑĺƯxūД̵nơʃĽá½M»›òmqóŘĝč˾ăC…ćāƿÝɽ©DZŅ¹đ¥˜³ðLrÁ®ɱĕģʼnǻ̋ȥơŻǛȡVï¹Ň۩ûkɗġƁ§ʇė̕ĩũƽō^ƕŠUv£ƁQï“Ƶkŏ½ΉÃŭdzLқʻ«ƭ\\lƒ‡ŭD‡“{ʓDkaFÃÄa“³ŤđÔGRÈƚhSӹŚsİ«ĐË[¥ÚDkº^Øg¼ŵ¸£EÍö•€ůʼnT¡c_‡ËKY‹ƧUśĵ„݃U_©rETÏʜ±OñtYw獃{£¨uM³x½şL©Ùá[ÓÐĥ Νtģ¢\\‚ś’nkO›w¥±ƒT»ƷFɯàĩÞáB¹Æ…ÑUw„੍žĽw[“mG½Èå~‡Æ÷QyŠěCFmĭZī—ŵVÁ™ƿQƛ—ûXS²‰b½KϽĉS›©ŷXĕŸ{ŽĕK·¥Ɨcqq©f¿]‡ßDõU³h—­gËÇïģÉɋw“k¯í}I·šœbmœÉ–ř›īJɥĻˁ×xo›ɹī‡l•c…¤³Xù]‘™DžA¿w͉ì¥wÇN·ÂËnƾƍdǧđ®Ɲv•Um©³G\\“}µĿ‡QyŹl㓛µEw‰LJQ½yƋBe¶ŋÀů‡ož¥A—˜Éw@•{Gpm¿Aij†ŽKLhˆ³`ñcËtW‚±»ÕS‰ëüÿďD‡u\\wwwù³—V›LŕƒOMËGh£õP¡™er™Ïd{“‡ġWÁ…č|yšg^ğyÁzÙs`—s|ÉåªÇ}m¢Ń¨`x¥’ù^•}ƒÌ¥H«‰Yªƅ”Aйn~Ꝛf¤áÀz„gŠÇDIԝ´AňĀ҄¶ûEYospõD[{ù°]u›Jq•U•|Soċxţ[õÔĥkŋÞŭZ˺óYËüċrw €ÞkrťË¿XGÉbřaDü·Ē÷Aê[Ää€I®BÕИÞ_¢āĠpŠÛÄȉĖġDKwbm‡ÄNô‡ŠfœƫVÉvi†dz—H‘‹QµâFšù­Âœ³¦{YGžƒd¢ĚÜO „€{Ö¦ÞÍÀPŒ^b–ƾŠlŽ[„vt×ĈÍE˨¡Đ~´î¸ùÎh€uè`¸ŸHÕŔVºwĠââWò‡@{œÙNÝ´ə²ȕn{¿¥{l—÷eé^e’ďˆXj©î\\ªÑò˜Üìc\\üqˆÕ[Č¡xoÂċªbØ­Œø|€¶ȴZdÆÂšońéŒGš\\”¼C°ÌƁn´nxšÊOĨ’ہƴĸ¢¸òTxÊǪMīИÖŲÃɎOvˆʦƢ~FއRěò—¿ġ~åŊœú‰Nšžš¸qŽ’Ę[Ĕ¶ÂćnÒPĒÜvúĀÊbÖ{Äî¸~Ŕünp¤ÂH¾œĄYÒ©ÊfºmԈĘcDoĬMŬ’˜S¤„s²‚”ʘچžȂVŦ –ŽèW°ªB|IJXŔþÈJĦÆæFĚêŠYĂªĂ]øªŖNÞüA€’fɨJ€˜¯ÎrDDšĤ€`€mz\\„§~D¬{vJÂ˜«lµĂb–¤p€ŌŰNĄ¨ĊXW|ų ¿¾ɄĦƐMT”‡òP˜÷fØĶK¢ȝ˔Sô¹òEð­”`Ɩ½ǒÂň×äı–§ĤƝ§C~¡‚hlå‚ǺŦŞkâ’~}ŽFøàIJaĞ‚fƠ¥Ž„Ŕdž˜®U¸ˆźXœv¢aƆúŪtŠųƠjd•ƺŠƺÅìnrh\\ĺ¯äɝĦ]èpĄ¦´LƞĬŠ´ƤǬ˼Ēɸ¤rºǼ²¨zÌPðŀbþ¹ļD¢¹œ\\ĜÑŚŸ¶ZƄ³àjĨoâŠȴLʉȮŒĐ­ĚăŽÀêZǚŐ¤qȂ\\L¢ŌİfÆs|zºeªÙæ§΢{Ā´ƐÚ¬¨Ĵà²łhʺKÞºÖTŠiƢ¾ªì°`öøu®Ê¾ãØ"
],
"encodeOffsets": [[88824, 50096]]
}
},
{
"type": "Feature",
"id": "110000",
"properties": {
"id": "110000",
"cp": [116.405285, 39.904989],
"name": "北京",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@ĽOÁ›ûtŷmiÍt_H»Ĩ±d`й­{bw…Yr“³S]§§o¹€qGtm_Sŧ€“oa›‹FLg‘QN_•dV€@Zom_ć\\ߚc±x¯oœRcfe…£’o§ËgToÛJíĔóu…|wP¤™XnO¢ÉˆŦ¯rNÄā¤zâŖÈRpŢZŠœÚ{GŠrFt¦Òx§ø¹RóäV¤XdˆżâºWbwڍUd®bêņ¾‘jnŎGŃŶŠnzÚSeîĜZczî¾i]͜™QaúÍÔiþĩȨWĢ‹ü|Ėu[qb[swP@ÅğP¿{\\‡¥A¨Ï‘Ѩj¯ŠX\\¯œMK‘pA³[H…īu}}"
],
"encodeOffsets": [[120023, 41045]]
}
},
{
"type": "Feature",
"id": "120000",
"properties": {
"id": "120000",
"cp": [117.190182, 39.125596],
"name": "天津",
"childNum": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
"@@ŬgX§Ü«E…¶Ḟ“¬O_™ïlÁg“z±AXe™µÄĵ{¶]gitgšIj·›¥îakS€‰¨ÐƎk}ĕ{gB—qGf{¿a†U^fI“ư‹³õ{YƒıëNĿžk©ïËZŏ‘R§òoY×Ógc…ĥs¡bġ«@dekąI[nlPqCnp{ˆō³°`{PNdƗqSÄĻNNâyj]äžÒD ĬH°Æ]~¡HO¾ŒX}ÐxŒgp“gWˆrDGˆŒpù‚Š^L‚ˆrzWxˆZ^¨´T\\|~@I‰zƒ–bĤ‹œjeĊªz£®Ĕvě€L†mV¾Ô_ȔNW~zbĬvG†²ZmDM~”~"
],
"encodeOffsets": [[120237, 41215]]
}
},
{
"type": "Feature",
"id": "310000",
"properties": {
"id": "310000",
"cp": [121.472644, 31.231706],
"name": "上海",
"childNum": 6
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@ɧư¬EpƸÁxc‡"],
["@@©„ªƒ"],
["@@”MA‹‘š"],
["@@Qp݁E§ÉC¾"],
["@@bŝՕÕEȣÚƥêImɇǦèÜĠŒÚžÃƌÃ͎ó"],
["@@ǜûȬɋŠŭ™×^‰sYŒɍDŋ‘ŽąñCG²«ªč@h–_p¯A{‡oloY€¬j@IJ`•gQڛhr|ǀ^MIJvtbe´R¯Ô¬¨YŽô¤r]ì†Ƭį"]
],
"encodeOffsets": [[[124702, 32062]], [[124547, 32200]], [[124808, 31991]], [[124726, 32110]], [[124903, 32376]], [[124438, 32149]]]
}
},
{
"type": "Feature",
"id": "500000",
"properties": {
"id": "500000",
"cp": [107.304962, 29.533155],
"name": "重庆",
"childNum": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
"@@vjG~nGŘŬĶȂƀƾ¹¸ØÎezĆT¸}êЖqHŸðqĖ䒊¥^CƒIj–²p…\\_ æüY|[YxƊæuž°xb®…Űb@~¢NQt°¶‚S栓Ê~rljĔëĚ¢~šuf`‘‚†fa‚ĔJåĊ„nÖ]„jƎćÊ@Š£¾a®£Ű{ŶĕF‹ègLk{Y|¡ĜWƔtƬJÑxq‹±ĢN´‰òK‰™–LÈüD|s`ŋ’ć]ƒÃ‰`đŒMûƱ½~Y°ħ`ƏíW‰½eI‹½{aŸ‘OIrÏ¡ĕŇa†p†µÜƅġ‘œ^ÖÛbÙŽŏml½S‹êqDu[R‹ãË»†ÿw`»y‘¸_ĺę}÷`M¯ċfCVµqʼn÷Z•gg“Œ`d½pDO‡ÎCnœ^uf²ènh¼WtƏxRGg¦…pV„†FI±ŽG^ŒIc´ec‡’G•ĹÞ½sëĬ„h˜xW‚}Kӈe­Xsbk”F¦›L‘ØgTkïƵNï¶}Gy“w\\oñ¡nmĈzjŸ•@™Óc£»Wă¹Ój“_m»ˆ¹·~MvÛaqœ»­‰êœ’\\ÂoVnŽÓØÍ™²«‹bq¿efE „€‹Ĝ^Qž~ Évý‡ş¤²Į‰pEİ}zcĺƒL‹½‡š¿gņ›¡ýE¡ya£³t\\¨\\vú»¼§·Ñr_oÒý¥u‚•_n»_ƒ•At©Þűā§IVeëƒY}{VPÀFA¨ąB}q@|Ou—\\Fm‰QF݅Mw˜å}]•€|FmϋCaƒwŒu_p—¯sfÙgY…DHl`{QEfNysBЦzG¸rHe‚„N\\CvEsÐùÜ_·ÖĉsaQ¯€}_U‡†xÃđŠq›NH¬•Äd^ÝŰR¬ã°wećJEž·vÝ·Hgƒ‚éFXjÉê`|yŒpxkAwœWĐpb¥eOsmzwqChóUQl¥F^laf‹anòsr›EvfQdÁUVf—ÎvÜ^efˆtET¬ôA\\œ¢sJŽnQTjP؈xøK|nBz‰„œĞ»LY‚…FDxӄvr“[ehľš•vN”¢o¾NiÂxGp⬐z›bfZo~hGi’]öF|‰|Nb‡tOMn eA±ŠtPT‡LjpYQ|†SH††YĀxinzDJ€Ìg¢và¥Pg‰_–ÇzII‹€II•„£®S¬„Øs쐣ŒN"
],
["@@ifjN@s"]
],
"encodeOffsets": [[[109628, 30765]], [[111725, 31320]]]
}
},
{
"type": "Feature",
"id": "810000",
"properties": {
"id": "810000",
"cp": [114.173355, 22.320048],
"name": "香港",
"childNum": 5
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
["@@AlBk"],
["@@mŽn"],
["@@EpFo"],
["@@ea¢pl¸Eõ¹‡hj[ƒ]ÔCΖ@lj˜¡uBXŸ…•´‹AI¹…[‹yDUˆ]W`çwZkmc–…M›žp€Åv›}I‹oJlcaƒfёKްä¬XJmРđhI®æÔtSHn€Eˆ„ÒrÈc"],
["@@rMUw‡AS®€e"]
],
"encodeOffsets": [[[117111, 23002]], [[117072, 22876]], [[117045, 22887]], [[116975, 23082]], [[116882, 22747]]]
}
},
{
"type": "Feature",
"id": "820000",
"properties": { "id": "820000", "cp": [113.54909, 22.198951], "name": "澳门", "childNum": 1 },
"geometry": {
"type": "Polygon",
"coordinates": ["@@kÊd°å§s"],
"encodeOffsets": [[116279, 22639]]
}
}
],
"UTF8Encoding": true
}
+189
View File
@@ -0,0 +1,189 @@
export const mapData: any = [
{
name: '北京',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '天津',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '上海',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '重庆',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '河北',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '河南',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '云南',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '辽宁',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '黑龙江',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '湖南',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '安徽',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '山东',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '新疆',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '江苏',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '浙江',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '江西',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '湖北',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '广西',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '甘肃',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '山西',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '内蒙古',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '陕西',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '吉林',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '福建',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '贵州',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '广东',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '青海',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '西藏',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '四川',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '宁夏',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '海南',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '台湾',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '香港',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
{
name: '澳门',
value: Math.round(Math.random() * 1000),
tipData: [Math.round(Math.random() * 1000), Math.round(Math.random() * 1000)],
},
];
export const getLineData = (() => {
const category: any[] = [];
let dottedBase = +new Date();
const lineData: any[] = [];
const barData: any[] = [];
for (let i = 0; i < 20; i++) {
const date = new Date((dottedBase += 1000 * 3600 * 24));
category.push([date.getFullYear(), date.getMonth() + 1, date.getDate()].join('-'));
const b = Math.random() * 200;
const d = Math.random() * 200;
barData.push(b);
lineData.push(d + b);
}
return { barData, category, lineData };
})();
+45
View File
@@ -0,0 +1,45 @@
<template>
<div ref="wrapRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, ref, nextTick, unref, onMounted } from 'vue';
import { useScript } from '/@/hooks/web/useScript';
const BAI_DU_MAP_URL = 'https://api.map.baidu.com/getscript?v=3.0&ak=OaBvYmKX3pjF7YFUFeeBCeGdy9Zp7xB2&services=&t=20210201100830&s=1';
export default defineComponent({
name: 'BaiduMap',
props: {
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: 'calc(100vh - 78px)',
},
},
setup() {
const wrapRef = ref<HTMLDivElement | null>(null);
const { toPromise } = useScript({ src: BAI_DU_MAP_URL });
async function initMap() {
await toPromise();
await nextTick();
const wrapEl = unref(wrapRef);
if (!wrapEl) return;
const BMap = (window as any).BMap;
const map = new BMap.Map(wrapEl);
const point = new BMap.Point(116.404, 39.915);
map.centerAndZoom(point, 15);
map.enableScrollWheelZoom(true);
}
onMounted(() => {
initMap();
});
return { wrapRef };
},
});
</script>
+47
View File
@@ -0,0 +1,47 @@
<template>
<div ref="wrapRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, ref, nextTick, unref, onMounted } from 'vue';
import { useScript } from '/@/hooks/web/useScript';
const A_MAP_URL = 'https://webapi.amap.com/maps?v=2.0&key=06313eb9c6563b674a8fd789db0692c3';
export default defineComponent({
name: 'AMap',
props: {
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: 'calc(100vh - 78px)',
},
},
setup() {
const wrapRef = ref<HTMLDivElement | null>(null);
const { toPromise } = useScript({ src: A_MAP_URL });
async function initMap() {
await toPromise();
await nextTick();
const wrapEl = unref(wrapRef);
if (!wrapEl) return;
const AMap = (window as any).AMap;
new AMap.Map(wrapEl, {
zoom: 11,
center: [116.397428, 39.90923],
viewMode: '3D',
});
}
onMounted(() => {
initMap();
});
return { wrapRef };
},
});
</script>
+52
View File
@@ -0,0 +1,52 @@
<template>
<div ref="wrapRef" :style="{ height, width }"></div>
</template>
<script lang="ts">
import { defineComponent, ref, nextTick, unref, onMounted } from 'vue';
import { useScript } from '/@/hooks/web/useScript';
const MAP_URL = 'https://maps.googleapis.com/maps/api/js?key=AIzaSyBQWrGwj4gAzKndcbwD5favT9K0wgty_0&signed_in=true';
export default defineComponent({
name: 'GoogleMap',
props: {
width: {
type: String,
default: '100%',
},
height: {
type: String,
default: 'calc(100vh - 78px)',
},
},
setup() {
const wrapRef = ref<HTMLDivElement | null>(null);
const { toPromise } = useScript({ src: MAP_URL });
async function initMap() {
await toPromise();
await nextTick();
const wrapEl = unref(wrapRef);
if (!wrapEl) return;
const google = (window as any).google;
const latLng = { lat: 116.404, lng: 39.915 };
const map = new google.maps.Map(wrapEl, {
zoom: 4,
center: latLng,
});
new google.maps.Marker({
position: latLng,
map: map,
title: 'Hello World!',
});
}
onMounted(() => {
initMap();
});
return { wrapRef };
},
});
</script>
+67
View File
@@ -0,0 +1,67 @@
<template>
<div>
<textarea ref="textarea">
白日依山尽黄河入海流
欲穷千里目更上一层楼
</textarea
>
</div>
</template>
<script lang="ts">
import { defineComponent, onMounted, ref, reactive } from 'vue';
// 引入全局实例
import _CodeMirror from 'codemirror';
// 核心样式
import 'codemirror/lib/codemirror.css';
// 引入主题后还需要在 options 中指定主题才会生效
import 'codemirror/theme/cobalt.css';
// 需要引入具体的语法高亮库才会有对应的语法高亮效果
// codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库
// 但 vue 貌似没有无法在实例初始化后再动态加载对应 JS ,所以此处才把对应的 JS 提前引入
import 'codemirror/mode/javascript/javascript.js';
import 'codemirror/mode/css/css.js';
import 'codemirror/mode/xml/xml.js';
import 'codemirror/mode/clike/clike.js';
import 'codemirror/mode/markdown/markdown.js';
import 'codemirror/mode/python/python.js';
import 'codemirror/mode/r/r.js';
import 'codemirror/mode/shell/shell.js';
import 'codemirror/mode/sql/sql.js';
import 'codemirror/mode/swift/swift.js';
import 'codemirror/mode/vue/vue.js';
// 尝试获取全局实例
export default defineComponent({
components: {},
setup() {
const CodeMirror = window.CodeMirror || _CodeMirror;
const textarea = ref(null);
const options = reactive({
// 缩进格式
tabSize: 2,
// 主题,对应主题库 JS 需要提前引入
theme: 'cobalt',
// 显示行号
lineNumbers: true,
line: true,
});
onMounted(() => {
init();
});
function init() {
CodeMirror.fromTextArea(textarea.value, options);
}
return {
textarea,
};
},
});
</script>
<style lang="css" scoped></style>
+110
View File
@@ -0,0 +1,110 @@
<template>
<PageWrapper
contentFullHeight
title="基础组件"
content=" 基础组件依赖于ant-design-vue,组件库已有的基础组件,项目中不会再次进行demo展示(二次封装组件除外)"
>
<a-row :gutter="[20, 20]">
<a-col :xl="10" :lg="24">
<a-card title="BasicButton Color">
<div class="my-2">
<h3>success</h3>
<div class="py-2">
<a-button color="success"> 成功 </a-button>
<a-button color="success" class="ml-2" disabled> 禁用 </a-button>
<a-button color="success" class="ml-2" loading> loading </a-button>
<a-button color="success" type="link" class="ml-2"> link </a-button>
<a-button color="success" type="link" class="ml-2" loading> loading link </a-button>
<a-button color="success" type="link" class="ml-2" disabled> disabled link </a-button>
</div>
</div>
<div class="my-2">
<h3>warning</h3>
<a-button color="warning"> 警告 </a-button>
<a-button color="warning" class="ml-2" disabled> 禁用 </a-button>
<a-button color="warning" class="ml-2" loading> loading </a-button>
<a-button color="warning" type="link" class="ml-2"> link </a-button>
<a-button color="warning" type="link" class="ml-2" loading> loading link </a-button>
<a-button color="warning" type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>error</h3>
<a-button color="error"> 错误 </a-button>
<a-button color="error" class="ml-2" disabled> 禁用 </a-button>
<a-button color="error" class="ml-2" loading> loading </a-button>
<a-button color="error" type="link" class="ml-2"> link </a-button>
<a-button color="error" type="link" class="ml-2" loading> loading link </a-button>
<a-button color="error" type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>ghost</h3>
<a-button ghost color="success" class="ml-2"> 幽灵成功 </a-button>
<a-button ghost color="warning" class="ml-2"> 幽灵警告 </a-button>
<a-button ghost color="error" class="ml-2"> 幽灵错误 </a-button>
<a-button ghost type="dashed" color="warning" class="ml-2"> 幽灵警告dashed </a-button>
<a-button ghost danger class="ml-2"> 幽灵危险 </a-button>
</div>
</a-card>
</a-col>
<a-col :xl="14" :lg="24">
<a-card title="BasicButton Types">
<div class="my-2">
<h3>primary</h3>
<a-button type="primary" preIcon="mdi:page-next-outline"> 主按钮 </a-button>
<a-button type="primary" class="ml-2" disabled> 禁用 </a-button>
<a-button type="primary" class="ml-2" danger preIcon="mdi:page-next-outline"> 危险 </a-button>
<a-button type="primary" class="ml-2" loading> loading </a-button>
<a-button type="link" class="ml-2"> link </a-button>
<a-button type="link" class="ml-2" loading> loading link </a-button>
<a-button type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>default</h3>
<a-button type="default"> 默认 </a-button>
<a-button type="default" class="ml-2" disabled> 禁用 </a-button>
<a-button type="default" class="ml-2" danger> 危险 </a-button>
<a-button type="default" class="ml-2" loading> loading </a-button>
<a-button type="link" class="ml-2"> link </a-button>
<a-button type="link" class="ml-2" loading> loading link </a-button>
<a-button type="link" class="ml-2" disabled> disabled link </a-button>
</div>
<div class="my-2">
<h3>dashed</h3>
<a-button type="dashed"> dashed </a-button>
<a-button type="dashed" class="ml-2" disabled> 禁用 </a-button>
<a-button type="dashed" class="ml-2" danger> 危险 </a-button>
<a-button type="dashed" class="ml-2" loading> loading </a-button>
</div>
<div class="my-2">
<h3>ghost 常规幽灵按钮通常用于有色背景下</h3>
<div class="bg-gray-400 py-2">
<a-button ghost type="primary" class="ml-2"> 幽灵主要 </a-button>
<a-button ghost type="default" class="ml-2"> 幽灵默认 </a-button>
<a-button ghost type="dashed" class="ml-2"> 幽灵dashed </a-button>
<a-button ghost type="primary" class="ml-2" disabled> 禁用 </a-button>
<a-button ghost type="primary" class="ml-2" loading> loading </a-button>
</div>
<!-- antd 按钮不能同时使用ghost和link -->
<!-- <a-button ghost type="link" class="ml-2"> link </a-button>-->
<!-- <a-button ghost type="link" class="ml-2" loading> loading link </a-button>-->
<!-- <a-button ghost type="link" class="ml-2" disabled> disabled link </a-button>-->
</div>
</a-card>
</a-col>
</a-row>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { Card, Row, Col } from 'ant-design-vue';
export default defineComponent({
components: { PageWrapper, ACard: Card, ARow: Row, ACol: Col },
});
</script>
+32
View File
@@ -0,0 +1,32 @@
<template>
<PageWrapper title="卡片列表示例" content="基础封装">
<CardList :params="params" :api="demoListApi" @getMethod="getMethod" @delete="handleDel">
<template #header>
<Button type="primary" color="error"> 按钮1 </Button>
<Button type="primary" color="success"> 按钮2 </Button>
</template>
</CardList>
</PageWrapper>
</template>
<script lang="ts" setup>
import { CardList } from '/@/components/CardList';
import { Button } from '/@/components/Button';
import { PageWrapper } from '/@/components/Page';
import { demoListApi } from '/@/api/demo/table';
import { useMessage } from '/@/hooks/web/useMessage';
const { notification } = useMessage();
// 请求api时附带参数
const params = {};
let reload = () => {};
// 获取内部fetch方法;
function getMethod(m: any) {
reload = m;
}
//删除按钮事件
function handleDel(id) {
console.log(id);
notification.success({ message: `成功删除${id}` });
reload();
}
</script>
+42
View File
@@ -0,0 +1,42 @@
<template>
<PageWrapper title="数字动画示例">
<Card>
<CardGrid class="count-to-demo-card">
<CountTo prefix="$" :color="'#409EFF'" :startVal="1" :endVal="200000" :duration="8000" />
</CardGrid>
<CardGrid class="count-to-demo-card">
<CountTo suffix="$" :color="'red'" :startVal="1" :endVal="300000" :decimals="2" :duration="6000" />
</CardGrid>
<CardGrid class="count-to-demo-card">
<CountTo suffix="$" :color="'rgb(0,238,0)'" :startVal="1" :endVal="400000" :duration="7000" />
</CardGrid>
<CardGrid class="count-to-demo-card">
<CountTo separator="-" :color="'rgba(138,43,226,.6)'" :startVal="10000" :endVal="500000" :duration="8000" />
</CardGrid>
</Card>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Card } from 'ant-design-vue';
import { CountTo } from '/@/components/CountTo/index';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: {
Card,
CardGrid: Card.Grid,
CountTo,
PageWrapper,
},
});
</script>
<style lang="less" scoped>
.count-to-demo {
&-card {
width: 50%;
font-size: 30px;
text-align: center;
}
}
</style>
+94
View File
@@ -0,0 +1,94 @@
<template>
<PageWrapper title="图片裁剪示例" content="需要开启测试接口服务才能进行上传测试!">
<CollapseContainer title="头像裁剪">
<CropperAvatar :uploadApi="uploadApi" :value="avatar" />
</CollapseContainer>
<CollapseContainer title="矩形裁剪" class="my-4">
<div class="container p-4">
<div class="cropper-container mr-10">
<CropperImage ref="refCropper" :src="img" @cropend="handleCropend" style="width: 40vw" />
</div>
<img :src="cropperImg" class="croppered" v-if="cropperImg" alt="" />
</div>
<p v-if="cropperImg">裁剪后图片信息{{ info }}</p>
</CollapseContainer>
<CollapseContainer title="圆形裁剪">
<div class="container p-4">
<div class="cropper-container mr-10">
<CropperImage ref="refCropper" :src="img" @cropend="handleCircleCropend" style="width: 40vw" circled />
</div>
<img :src="circleImg" class="croppered" v-if="circleImg" />
</div>
<p v-if="circleImg">裁剪后图片信息{{ circleInfo }}</p>
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { CollapseContainer } from '/@/components/Container';
import { CropperImage, CropperAvatar } from '/@/components/Cropper';
import { uploadApi } from '/@/api/sys/upload';
import img from '/@/assets/images/header.jpg';
import { useUserStore } from '/@/store/modules/user';
export default defineComponent({
components: {
PageWrapper,
CropperImage,
CollapseContainer,
CropperAvatar,
},
setup() {
const info = ref('');
const cropperImg = ref('');
const circleInfo = ref('');
const circleImg = ref('');
const userStore = useUserStore();
const avatar = ref(userStore.getUserInfo?.avatar || '');
function handleCropend({ imgBase64, imgInfo }) {
info.value = imgInfo;
cropperImg.value = imgBase64;
}
function handleCircleCropend({ imgBase64, imgInfo }) {
circleInfo.value = imgInfo;
circleImg.value = imgBase64;
}
return {
img,
info,
circleInfo,
cropperImg,
circleImg,
handleCropend,
handleCircleCropend,
avatar,
uploadApi: uploadApi as any,
};
},
});
</script>
<style scoped>
.container {
display: flex;
width: 100vw;
align-items: center;
}
.cropper-container {
width: 40vw;
}
.croppered {
height: 360px;
}
p {
margin: 10px;
}
</style>
+79
View File
@@ -0,0 +1,79 @@
<template>
<PageWrapper title="详情组件示例">
<Description title="基础示例" :collapseOptions="{ canExpand: true, helpMessage: 'help me' }" :column="3" :data="mockData" :schema="schema" />
<Description
class="mt-4"
title="垂直示例"
layout="vertical"
:collapseOptions="{ canExpand: true, helpMessage: 'help me' }"
:column="2"
:data="mockData"
:schema="schema"
/>
<Description @register="register" class="mt-4" />
<Description @register="register1" class="mt-4" />
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Description, DescItem, useDescription } from '/@/components/Description/index';
import { PageWrapper } from '/@/components/Page';
const mockData: Recordable = {
username: 'test',
nickName: 'VB',
age: '123',
phone: '15695909xxx',
email: '190848757@qq.com',
addr: '厦门市思明区',
sex: '男',
certy: '3504256199xxxxxxxxx',
tag: 'orange',
};
const schema: DescItem[] = [
{
field: 'username',
label: '用户名',
},
{
field: 'nickName',
label: '昵称',
render: (curVal, data) => {
return `${data.username}-${curVal}`;
},
},
{
field: 'phone',
label: '联系电话',
},
{
field: 'email',
label: '邮箱',
},
{
field: 'addr',
label: '地址',
},
];
export default defineComponent({
components: { Description, PageWrapper },
setup() {
const [register] = useDescription({
title: 'useDescription',
data: mockData,
schema: schema,
});
const [register1] = useDescription({
title: '无边框',
bordered: false,
data: mockData,
schema: schema,
});
return { mockData, schema, register, register1 };
},
});
</script>
+13
View File
@@ -0,0 +1,13 @@
<template>
<BasicDrawer v-bind="$attrs" title="Drawer Title" width="50%"> Drawer Info. </BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
return {};
},
});
</script>
+17
View File
@@ -0,0 +1,17 @@
<template>
<BasicDrawer v-bind="$attrs" @register="register" title="Drawer Title" width="50%">
Drawer Info.
<a-button type="primary" @click="closeDrawer"> 内部关闭drawer </a-button>
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
const [register, { closeDrawer }] = useDrawerInner();
return { register, closeDrawer };
},
});
</script>
+35
View File
@@ -0,0 +1,35 @@
<template>
<BasicDrawer v-bind="$attrs" title="Modal Title" width="50%" showFooter @ok="handleOk">
<p class="h-20" v-for="index in 40" :key="index"> 根据屏幕高度自适应 </p>
<template #insertFooter>
<a-button> btn</a-button>
</template>
<template #centerFooter>
<a-button> btn2</a-button>
</template>
<template #appendFooter>
<a-button> btn3</a-button>
</template>
<!-- <template #footer>
<a-button> customerFooter</a-button>
</template> -->
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
setup() {
return {
handleOk: () => {
console.log('=====================');
console.log('ok');
console.log('======================');
},
};
},
});
</script>
+53
View File
@@ -0,0 +1,53 @@
<template>
<BasicDrawer v-bind="$attrs" @register="register" title="Drawer Title" width="50%">
<div>
<BasicForm @register="registerForm" />
</div>
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 12,
},
defaultValue: '111',
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 12,
},
},
];
export default defineComponent({
components: { BasicDrawer, BasicForm },
setup() {
const [registerForm, { setFieldsValue }] = useForm({
labelWidth: 120,
schemas,
showActionButtonGroup: false,
actionColOptions: {
span: 24,
},
});
const [register] = useDrawerInner((data) => {
// 方式1
setFieldsValue({
field2: data.data,
field1: data.info,
});
});
return { register, schemas, registerForm };
},
});
</script>
+13
View File
@@ -0,0 +1,13 @@
<template>
<BasicDrawer v-bind="$attrs" :isDetail="true" title="Drawer Title5">
<p class="h-20"> Content Message </p>
<template #titleToolbar> toolbar </template>
</BasicDrawer>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicDrawer } from '/@/components/Drawer';
export default defineComponent({
components: { BasicDrawer },
});
</script>
+69
View File
@@ -0,0 +1,69 @@
<template>
<PageWrapper title="抽屉组件使用示例">
<Alert message="使用 useDrawer 进行抽屉操作" show-icon />
<a-button type="primary" class="my-4" @click="openDrawerLoading"> 打开Drawer </a-button>
<Alert message="内外同时控制显示隐藏" show-icon />
<a-button type="primary" class="my-4" @click="openDrawer2(true)"> 打开Drawer </a-button>
<Alert message="自适应高度/显示footer" show-icon />
<a-button type="primary" class="my-4" @click="openDrawer3(true)"> 打开Drawer </a-button>
<Alert message="内外数据交互" show-icon />
<a-button type="primary" class="my-4" @click="send"> 打开Drawer并传递数据 </a-button>
<Alert message="详情页模式" show-icon />
<a-button type="primary" class="my-4" @click="openDrawer5(true)"> 打开详情Drawer </a-button>
<Drawer1 @register="register1" />
<Drawer2 @register="register2" />
<Drawer3 @register="register3" />
<Drawer4 @register="register4" />
<Drawer5 @register="register5" />
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert } from 'ant-design-vue';
import { useDrawer } from '/@/components/Drawer';
import Drawer1 from './Drawer1.vue';
import Drawer2 from './Drawer2.vue';
import Drawer3 from './Drawer3.vue';
import Drawer4 from './Drawer4.vue';
import Drawer5 from './Drawer5.vue';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { Alert, PageWrapper, Drawer1, Drawer2, Drawer3, Drawer4, Drawer5 },
setup() {
const [register1, { openDrawer: openDrawer1, setDrawerProps }] = useDrawer();
const [register2, { openDrawer: openDrawer2 }] = useDrawer();
const [register3, { openDrawer: openDrawer3 }] = useDrawer();
const [register4, { openDrawer: openDrawer4 }] = useDrawer();
const [register5, { openDrawer: openDrawer5 }] = useDrawer();
function send() {
openDrawer4(true, {
data: 'content',
info: 'Info',
});
}
function openDrawerLoading() {
openDrawer1();
setDrawerProps({ loading: true });
setTimeout(() => {
setDrawerProps({ loading: false });
}, 2000);
}
return {
register1,
openDrawer1,
register2,
openDrawer2,
register3,
openDrawer3,
register4,
register5,
openDrawer5,
send,
openDrawerLoading,
};
},
});
</script>
+464
View File
@@ -0,0 +1,464 @@
<template>
<div>
<div class="legend">
<div class="legendBox">
<div class="legendSml legendColor1"></div>
<div>计划时间</div>
</div>
<div class="legendBox">
<div class="legendSml legendColor2"></div>
<div>相交时间</div>
</div>
<div class="legendBox">
<div class="legendSml legendColor3"></div>
<div>实际时间</div>
</div>
</div>
<div ref="gantt" class="left-container" />
</div>
</template>
<script>
import dayjs from 'dayjs' // dayjs格式化时间
import gantt from "dhtmlx-gantt"; // 引入模块
import "dhtmlx-gantt/codebase/dhtmlxgantt.css"; //皮肤
export default {
name: "gantt",
data() {
return {
tasks: {
data: [],
},
};
},
mounted() {
this.initData();
gantt.config.show_progress = false;
function percenToString(num) {
return Math.floor(num * 100) + "%";
}
// 设置每一部分的颜色
function renderLabel(progress, type, state) {
var relWidth = progress * 100;
var cssClass = "custom_progress ";
if (type === 1) {
cssClass += "nearly_done";
} else if (type === 2) {
if (state) {
cssClass += "in_progress";
} else {
cssClass += "in_progress_w";
}
} else {
cssClass += "idle";
}
return (
"<div class='" +
cssClass +
"' style='width:" +
relWidth +
"%'>" +
percenToString(progress) +
"</div>"
);
}
// 根据定义类型判断颜色顺序
gantt.templates.task_text = function(start, end, task) {
let colorType = task.colorType
if (colorType === 'j12' || colorType === 'one') {
return (
renderLabel(task.progress1, 1, task.state) +
renderLabel(task.progress2, 2, task.state) +
renderLabel(task.progress3, 3, task.state)
);
} else if (colorType === 'j21') {
return (
renderLabel(task.progress1, 3, task.state) +
renderLabel(task.progress2, 2, task.state) +
renderLabel(task.progress3, 1, task.state)
);
} else if (colorType === 'b12') {
return (
renderLabel(task.progress1, 1, task.state) +
renderLabel(task.progress2, 2, task.state) +
renderLabel(task.progress3, 1, task.state)
);
} else if (colorType === 'b21') {
return (
renderLabel(task.progress1, 3, task.state) +
renderLabel(task.progress2, 2, task.state) +
renderLabel(task.progress3, 3, task.state)
);
} else if (colorType === 'l12') {
return (
renderLabel(task.progress1, 1, task.state) +
renderLabel(task.progress2, 2, task.state) +
renderLabel(task.progress3, 3, task.state)
);
} else if (colorType === 'l21') {
return (
renderLabel(task.progress1, 3, task.state) +
renderLabel(task.progress2, 2, task.state) +
renderLabel(task.progress3, 1, task.state)
);
} else {
return (
renderLabel(task.progress1, 1, task.state) +
renderLabel(task.progress2, 2, task.state) +
renderLabel(task.progress3, 3, task.state)
);
}
};
gantt.plugins({ marker: true, tooltip: true }); // 开启marker插件
var dateToStr = gantt.date.date_to_str(gantt.config.task_date);
var today = new Date();
// 添加固定时间线
gantt.addMarker({
start_date: today,
css: 'today',
text: '今日:' + dayjs(new Date()).format('YYYY-MM-DD'),
title: 'Today: ' + dateToStr(today)
});
// 提示框内容
gantt.templates.tooltip_text = function(start, end, task){
return "<b>任务内容:</b>" + task.text +
"<br/><b>计划开始:</b> " + task.planStart +
"<br/><b>计划结束:</b> " + task.planEnd +
"<br/><b>实际开始:</b> " + task.actualStart +
"<br/><b>实际结束:</b> " + task.actualEnd
};
gantt.config.scale_unit = "year";
gantt.config.step = 1;
gantt.config.date_scale = "%Y";
//当右侧不止显示年份时,可以添加展示月日,添加一个就加一行
gantt.config.subscales = [
{ unit: "month", step: 1, date: "%m" },
// { unit: "day", step: 1, date: "%d" },
];
//甘特图右侧表头的高度
gantt.config.scale_height = 80;
//使用中文
gantt.i18n.setLocale("cn");
//自适应甘特图的尺寸大小, 使得在不出现滚动条的情况下, 显示全部任务
gantt.config.autosize = true;
//只读模式
gantt.config.readonly = false;
//是否显示左侧树表格
gantt.config.show_grid = true;
//表格列设置
gantt.config.columns = [
{ name: "text", label: "任务内容", width: "120", align: "center" },
{ name: "planStart", label: "计划开始", width: "100", align: "center" },
{ name: "planEnd", label: "计划结束", width: "100", align: "center" },
{ name: "actualStart", label: "实际开始", width: "100", align: "center" },
{ name: "actualEnd", label: "实际结束", width: "100", align: "center" },
];
//任务条上的文字大小 以及取消border自带样式
gantt.templates.task_class = function() {
return "firstLevelTask"
};
// 初始化
gantt.init(this.$refs.gantt);
// 数据解析
gantt.parse(this.tasks);
gantt.attachEvent("onGanttRender", () => {
let timer = 0;
// 【#8104】监听甘特图鼠标移出,关闭tooltip
this.$refs.gantt.addEventListener("mouseleave", () => {
if (typeof gantt.ext.tooltips?.tooltip?.hide === 'function') {
const tooltipTimeout = gantt.config.tooltip_timeout ?? 30
timer = setTimeout(() => gantt.ext.tooltips.tooltip.hide(), tooltipTimeout + 50)
}
});
this.$refs.gantt.addEventListener("mouseenter", () => {
if (timer) {
clearTimeout(timer)
}
});
});
},
methods: {
initData() {
// 处理数据
let arr = [{
id: '1',
task: '12相交',
planStart: '2022-09-01',
planEnd: '2022-12-01',
actualStart: '2022-10-01',
actualEnd: '2022-02-01',
}, {
id: '2',
task: '21相交',
planStart: '2022-10-01',
planEnd: '2022-02-01',
actualStart: '2022-09-01',
actualEnd: '2022-12-01',
}, {
id: '3',
task: '1包含2',
planStart: '2022-09-01',
planEnd: '2022-05-01',
actualStart: '2022-12-01',
actualEnd: '2022-02-01',
}, {
id: '4',
task: '2包含1',
planStart: '2022-12-01',
planEnd: '2022-02-01',
actualStart: '2022-09-01',
actualEnd: '2022-05-01',
}, {
id: '5',
task: '12相离',
planStart: '2022-09-01',
planEnd: '2022-12-01',
actualStart: '2022-02-01',
actualEnd: '2022-05-01',
}, {
id: '6',
task: '21相离',
planStart: '2022-02-01',
planEnd: '2022-05-01',
actualStart: '2022-09-01',
actualEnd: '2022-12-01',
}, {
id: '7',
task: '1包含2',
planStart: '2022-09-01',
planEnd: '2022-05-01',
actualStart: '2022-09-01',
actualEnd: '2022-02-01',
}, {
id: '8',
task: '1包含2',
planStart: '2023-09-01',
planEnd: '2022-05-01',
actualStart: '2022-12-01',
actualEnd: '2022-05-01',
}, {
id: '9',
task: '2包含1',
planStart: '2022-09-01',
planEnd: '2022-02-01',
actualStart: '2022-09-01',
actualEnd: '2022-05-01',
}, {
id: '10',
task: '2包含1',
planStart: '2022-12-01',
planEnd: '2022-05-01',
actualStart: '2022-09-01',
actualEnd: '2022-05-01',
}, {
id: '11',
task: '12相等',
planStart: '2022-09-01',
planEnd: '2022-05-01',
actualStart: '2022-09-01',
actualEnd: '2022-05-01',
}];
let newArr = []
arr.forEach((item, index) => {
let obj = this.getDate(item.planStart, item.planEnd, item.actualStart, item.actualEnd)
newArr.push({
id: item.id,
text: item.task,
start_date: new Date(dayjs(obj.minTime).format("YYYY-MM-DD")),
progress1: Math.abs(obj.progress1),
progress2: Math.abs(obj.progress2),
progress3: Math.abs(obj.progress3),
duration: obj.duration,
planStart: item.planStart,
planEnd: item.planEnd,
actualStart: item.actualStart,
actualEnd: item.actualEnd,
state: obj.state,
colorType: obj.colorType
})
});
this.tasks.data = newArr
},
// 时间计算
getDate(Atime, Btime, Ctime, Dtime) {
let obj = {};
let arr = [Atime, Btime, Ctime, Dtime];
let timeArr = [];
arr.forEach((item) => {
timeArr.push(new Date(item));
});
let minTime = dayjs(Math.min(...timeArr)).format("MM-DD-YYYY");
let maxTime = dayjs(Math.max(...timeArr)).format("YYYY-MM-DD");
let duration = this.difference(minTime, maxTime);
let progress1 = 0;
let progress2 = 0;
let progress3 = 0;
let state = true;
let colorType = true;
if (Atime < Ctime && Ctime < Btime && Btime < Dtime) {
// console.log('12相交', index)
colorType = 'j12'
progress2 = (this.difference(Ctime, Btime) / duration)
progress1 = (this.difference(Atime, Ctime) / duration)
progress3 = (this.difference(Btime, Dtime) / duration)
} else if (Btime > Ctime && Btime >= Dtime && Ctime >= Atime && Dtime > Atime) {
// console.log('1包含2', index)
colorType = 'b12'
state = true
progress2 = (this.difference(Ctime, Dtime) / duration)
progress1 = (this.difference(Atime, Ctime) / duration)
progress3 = (this.difference(Dtime, Btime) / duration)
} else if (Dtime >= Btime && Dtime > Atime && Atime >= Ctime && Btime > Ctime) {
// console.log('2包含1', index)
colorType = 'b21'
state = true
progress2 = (this.difference(Atime, Btime) / duration)
progress1 = (this.difference(Ctime, Atime) / duration)
progress3 = (this.difference(Btime, Dtime) / duration)
} else if (Btime > Dtime && Dtime > Atime && Atime > Ctime) {
// console.log('21相交', index)
colorType = 'j21'
state = true
progress2 = (this.difference(Atime, Dtime) / duration)
progress1 = (this.difference(Ctime, Atime) / duration)
progress3 = (this.difference(Dtime, Btime) / duration)
} else if (Btime < Ctime) {
// console.log('12相离')
colorType = 'l12'
state = false
progress2 = (this.difference(Btime, Ctime) / duration)
progress1 = (this.difference(Atime, Btime) / duration)
progress3 = (this.difference(Ctime, Dtime) / duration)
} else if (Dtime < Atime) {
// console.log('21相离')
colorType = 'l21'
state = false
progress2 = (this.difference(Dtime, Atime) / duration)
progress3 = (this.difference(Atime, Btime) / duration)
progress1 = (this.difference(Ctime, Dtime) / duration)
} else {
colorType = 'one'
console.log('状态错误')
}
obj = {
state: state,
minTime: minTime,
duration: duration * 1,
progress1: progress1 * 1,
progress2: progress2 * 1,
progress3: progress3 * 1,
colorType: colorType,
};
return obj;
},
difference(beginTime, Dtime) {
var dateBegin = new Date(beginTime);
var dateEnd = new Date(Dtime);
var dateDiff = dateEnd.getTime() - dateBegin.getTime(); //时间差的毫秒数
var dayDiff = Math.floor(dateDiff / (24 * 3600 * 1000)); //计算出相差天数
return dayDiff;
}
}
}
</script>
<style lang="less" scoped>
html,
body {
height: 100%;
padding: 0px;
margin: 0px;
overflow: hidden;
}
.legend {
padding: 10px;
display: flex;
.legendBox {
display: flex;
align-items: center;
margin-right: 20px;
.legendSml {
width: 30px;
height: 16px;
border-radius: 5px;
margin-right: 4px;
}
.legendColor1 {
background: #01aff4;
}
.legendColor2 {
background: #0169bd;
}
.legendColor3 {
background: rgb(1, 105 , 189, 50%);
}
}
}
.gantt_task_content {
display: flex;
}
.gantt_task_line {
background: #fff;
}
.custom_progress {
display: inline-block;
vertical-align: top;
text-align: center;
height: 100%;
}
.nearly_done {
background-color: #01aff4 !important;
}
.in_progress {
background-color: #0169bd !important;
}
.in_progress_w {
background-color: #FFF !important;
}
.idle {
background-color: rgb(1, 105 , 189, 50%) !important;
}
.firstLevelTask {
border: none;
}
.secondLevelTask {
border: none;
}
.left-container {
height: 600px;
}
/** 去掉单元格线 */
.gantt_task_cell{
border: none;
}
</style>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 579 B

@@ -0,0 +1,334 @@
<template>
<div class="node">
<div
class="box"
@mouseleave="
() => {
isShow = false;
}
"
>
<div class="card">
<div class="userInfo">
<img class="avatar" :src="item[avatar]" alt="" />
<div class="info">
<p class="name">{{ item[fullname] }}</p>
<p class="department">{{ item[department] }}</p>
<p class="position">{{ item[job] }}</p>
</div>
<div v-if="item[subTotalCount]" class="count">
<span class="icon"></span>
<span class="text">{{ item[subTotalCount] }}</span>
</div>
</div>
</div>
<div class="result">
<i
class="icon"
@click="
() => {
isShow = true;
}
"
></i>
<ul v-if="isShow" class="options">
<li @click="handleAdd(item, parentNode)">添加下属</li>
<li @click="handleReplace(item, parentNode)">替换成员</li>
<li @click="handleDel(item, parentNode)">移除成员</li>
</ul>
</div>
<div v-if="item[hasSub]" class="toggle" :class="[isExpand ? 'minus' : 'add']" @click="handleExpand(item)"></div>
</div>
<nodeWrap
v-if="item[children]?.length && (isExpand || isExpand === null)"
:treeSource="item[children]"
:parentNode="item"
:readonly="readonly"
@del="handleDel"
@replace="handleReplace"
@add="handleAdd"
:loadData="loadData"
:fullname="fullname"
:children="children"
:avatar="avatar"
:accountId="accountId"
:department="department"
:job="job"
:subTotalCount="subTotalCount"
:hasSub="hasSub"
></nodeWrap>
<p v-if="tips?.status && isExpand" class="tips" :class="tips.status">{{ tips.text }}</p>
</div>
</template>
<script setup name="node">
import nodeWrap from '../nodeWrap/index.vue';
import { computed, ref, watch } from 'vue';
const props = defineProps({
item: {
type: Object,
default: () => {}
},
fullname: {
type: String,
default: 'fullname'
},
avatar: {
type: String,
default: 'avatar'
},
accountId: {
type: String,
default: 'accountId'
},
department: {
type: String,
default: '-department'
},
job: {
type: String,
default: 'job'
},
subTotalCount: {
type: String,
default: 'subTotalCount'
},
hasSub: {
type: String,
default: 'hasSub'
},
children: {
type: String,
default: 'children'
},
readonly: {
type: Boolean,
default: false
},
loadData: {
type: Function,
default: () => ({})
},
treeSource: {
type: Array,
default: () => []
},
rootNode: {
type: Boolean,
default: false
},
parentNode: {
default: null
}
});
let emit = defineEmits(['add', 'del', 'replace']);
const isShow = ref(false);
// true:展开;false:收缩;null:当下级有值的时候默认展开(1.新增时下级需展开;2.查看是默认得展开)
const isExpand = ref(null);
const tips = ref(null);
const handleExpand = item => {
isExpand.value = !isExpand.value;
if (!item[props.children]?.length && isExpand.value) {
tips.value = { status: 'loading', text: '正在加载中...' };
props
.loadData(item)
.then(() => {
tips.value = { status: 'succes', text: '' };
})
.catch(() => {
tips.value = { status: 'error', text: '加载失败,请重试~' };
});
}
};
const handleAdd = item => {
emit('add', item);
};
const handleReplace = (item, parentNode) => {
emit('replace', item, parentNode);
};
const handleDel = (item, parentNode) => {
emit('del', item, parentNode);
};
</script>
<style lang="less" scoped>
p { margin-bottom: 0;}
.node {
padding-top: 10px;
position: relative;
&::before {
border-bottom: 1px solid #d3d3d3;
content: '';
display: inline-block;
position: absolute;
top: 50px;
width: 40px;
}
&:not(&:last-child) {
border-left: 1px solid #d3d3d3;
}
&:last-child {
&::after {
border-left: 1px solid #d3d3d3;
content: '';
display: inline-block;
height: 50px;
left: 0;
position: absolute;
top: 0;
}
}
}
.box {
display: inline-block;
padding-right: 40px;
position: relative;
&:hover {
.result {
display: block;
}
}
}
.card {
border: 1px solid transparent;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
display: inline-block;
height: 80px;
margin-left: 40px;
padding: 0 6px;
width: 240px;
&:hover {
border-color: #1e88e5;
}
}
.userInfo {
box-sizing: initial;
display: flex;
flex-direction: row;
height: 60px;
padding: 10px 0;
.avatar {
align-self: center;
border-radius: 50%;
flex: 0 0 48px;
height: 48px;
margin-left: 10px;
}
.info {
display: flex;
flex: 1 0 0%;
flex-direction: column;
margin-left: 16px;
padding-right: 10px;
.name {
color: #333;
font-size: 15px;
margin-bottom: 4px;
max-width: 100px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.department,
.position {
color: #9e9e9e;
flex: 1 0 0%;
font-size: 12px;
max-width: 155px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.count {
position: absolute;
right: 50px;
top: 10px;
display: flex;
align-items: center;
line-height: 14px;
.icon {
width: 14px;
height: 14px;
background-image: url(../../asstes/person.png);
background-size: 100% auto;
}
.text {
font-size: 13px;
margin-left: 2px;
color: #333;
}
}
}
.result {
display: none;
.icon {
background-image: url(../../asstes/omit.png);
background-size: 100% 100%;
height: 20px;
width: 20px;
right: 4px;
top: 30px;
position: absolute;
cursor: pointer;
}
.options {
background: #fff;
border-radius: 2px;
box-shadow: 0 2px 5px 3px #ddd;
line-height: 30px;
padding: 6px 0;
position: absolute;
right: -120px;
top: 30px;
width: 120px;
z-index: 1;
line-height: 32px;
li {
text-align: center;
cursor: pointer;
&:hover {
background-color: #1890ff;
color: #fff;
}
}
}
}
.toggle {
position: absolute;
top: 50%;
left: -10px;
transform: translate(0, -50%);
width: 20px;
height: 20px;
background-size: 100% auto;
background-color: #fff;
border-radius: 50%;
cursor: pointer;
z-index: 1;
&.add {
background-image: url(../../asstes/add.png);
}
&.minus {
background-image: url(../../asstes/minus.png);
}
}
.tips {
margin-left: 40px;
width: 240px;
line-height: 40px;
text-align: center;
&.error {
color: #a7a1a2;
}
&.loading {
color: #1890ff;
}
&.success {
display: none;
}
}
</style>
@@ -0,0 +1,49 @@
<template>
<div class="nodeWrap" :class="[rootNode && 'rootNodeWrap']">
<node
v-for="(item, index) in treeSource"
:parentNode="parentNode"
:accountId="accountId"
:key="item[accountId]"
:item="item"
v-bind="$attrs"
></node>
</div>
</template>
<script setup name="nodeWrap">
import node from '../node/index.vue';
import { computed, ref, watch } from 'vue';
const props = defineProps({
accountId: {
type: String,
default: 'accountId'
},
children: {
type: String,
default: 'children'
},
rootNode: {
type: Boolean,
default: false
},
parentNode: {
type: Object || Null,
default: null
},
treeSource: {
type: Array,
default: () => []
}
});
//let emit = defineEmits(['add', 'del', 'replace']);
</script>
<style lang="less" scoped>
.nodeWrap {
padding-left: 80px;
&.rootNodeWrap {
padding-left: 20px;
}
}
</style>
@@ -0,0 +1,71 @@
<template>
<div class="reportTree">
<div class="root">
<p class="company">{{ companyName }}</p>
<p class="add">
<span class="icon"></span>
<span v-if="!readonly" class="text" @click="addHandle({ isRoot: true })">添加下属</span>
</p>
</div>
<p v-if="treeSourceLen" class="line"></p>
<nodeWrap
v-if="treeSourceLen"
:readonly="readonly"
:treeSource="treeSource"
rootNode
@add="addHandle"
v-bind="$attrs"
></nodeWrap>
</div>
</template>
<script setup name="reportTree">
import nodeWrap from './components/nodeWrap/index.vue';
import { computed } from 'vue';
const props = defineProps({
companyName: {
default: ''
},
readonly: {
type: Boolean,
default: false
},
treeSource: {
type: Array,
default: []
}
});
const emit = defineEmits(['add']);
const treeSourceLen = computed(() => props.treeSource.length);
const addHandle = (...arg) => {
console.log('reportTree');
emit('add', ...arg);
};
</script>
<style lang="less" scoped>
p { margin-bottom: 0; }
.reportTree {
height: 100%;
overflow: auto;
}
.root {
display: flex;
align-items: center;
line-height: 36px;
height: 36px;
}
.company {
margin: 0 10px;
font-size: 16px;
}
.add {
color: #9e9e9e;
cursor: pointer;
}
.line {
border-left: 1px solid #d3d3d3;
height: 20px;
margin-left: 20px;
}
</style>
+89
View File
@@ -0,0 +1,89 @@
export const resData = {
data: {
totalCount: 1,
pagedDatas: [
{
fullname: '神经蛙',
accountId: '037dcdf6-7a96-4627-bc1c-116ee2d6b0b8',
avatar:
'https://p1.mingdaoyun.cn/UserAvatar/31623f8a-c75d-4a69-a532-0b04ad0ac8a8.jpg?imageView2/1/w/100/h/100/q/90',
department: '总裁办',
job: 'CEO',
status: 1,
subTotalCount: 5,
hasSub: true
},
{
fullname: '神经蛙01',
accountId: '037dcdf6-7a96-4627-bc1c-116ee2d6b0b01',
avatar:
'https://p1.mingdaoyun.cn/UserAvatar/31623f8a-c75d-4a69-a532-0b04ad0ac8a8.jpg?imageView2/1/w/100/h/100/q/90',
department: '总裁办',
job: 'CEO',
status: 1,
subTotalCount: 4,
hasSub: true
},
{
fullname: '神经蛙02',
accountId: '037dcdf6-7a96-4627-bc1c-116ee2d6b0-02',
avatar:
'https://p1.mingdaoyun.cn/UserAvatar/31623f8a-c75d-4a69-a532-0b04ad0ac8a8.jpg?imageView2/1/w/100/h/100/q/90',
department: '总裁办',
job: 'CEO',
status: 1,
subTotalCount: 4,
hasSub: true
}
]
},
state: 1
};
export const resLookData = {
data: {
totalCount: 1,
pagedDatas: [
{
fullname: '神经蛙02',
accountId: '037dcdf6-7a96-4627-bc1c-116ee2d6b0-02',
avatar:
'https://p1.mingdaoyun.cn/UserAvatar/31623f8a-c75d-4a69-a532-0b04ad0ac8a8.jpg?imageView2/1/w/100/h/100/q/90',
department: '总裁办',
job: 'CEO',
status: 1,
subTotalCount: 1,
hasSub: true,
children: [
{
parentId: '037dcdf6-7a96-4627-bc1c-116ee2d6b0-02',
fullname: '小仙女01',
accountId: '2c6d9882-b26f-4039-9119-5f9ff03-02-01',
avatar:
'https://p1.mingdaoyun.cn/UserAvatar/default9.png?watermark/2/text/5LuZ5aWz/font/5b6u6L2v6ZuF6buR/fontsize/700/fill/d2hpdGU=/dissolve/100/gravity/Center/dx/0/dy/0%7CimageView2/1/w/100/h/100/q/90',
department: '秘书办',
job: '董事会秘书',
status: 1,
subTotalCount: 0,
hasSub: true,
children: [
{
parentId: '037dcdf6-7a96-4627-bc1c-116ee2d6b0-03',
fullname: '查看的级数',
accountId: '2c6d9882-b26f-4039-9119-5f9ff03-03-01',
avatar:
'https://p1.mingdaoyun.cn/UserAvatar/default9.png?watermark/2/text/5LuZ5aWz/font/5b6u6L2v6ZuF6buR/fontsize/700/fill/d2hpdGU=/dissolve/100/gravity/Center/dx/0/dy/0%7CimageView2/1/w/100/h/100/q/90',
department: '秘书办',
job: '董事会秘书',
status: 1,
subTotalCount: 1,
hasSub: true
}
]
}
]
}
]
},
state: 1
};
+137
View File
@@ -0,0 +1,137 @@
<template>
<div class="container">
<a-button type="primary" @click="lookHandle">查看</a-button>
<reportTree
v-if="show"
:companyName="companyName"
:treeSource="treeSource"
:readonly="readonly"
:loadData="onLoadData"
department="department"
@add="addHandle"
@replace="replaceHandle"
@del="delHandle"
></reportTree>
</div>
</template>
<script setup>
import { reactive, ref, nextTick } from 'vue';
import { cloneDeep } from 'lodash-es';
import reportTree from './components/reportTree/index.vue';
import { resData, resLookData } from './data.js';
console.log(cloneDeep({ a: 'name' }));
const companyName = '天脉聚源(攸县)有限公司';
const treeSource = ref(resData.data.pagedDatas);
const readonly = ref(false);
const show = ref(true);
//添加下属
const addHandle = data => {
let res = toDemo();
res['accountId'] = uuid();
if (data['isRoot']) {
delete res['parentId'];
delete res['subTotalCount'];
delete res['hasSub'];
treeSource.value.unshift(res);
} else {
res['parentId'] = data['accountId'];
if (data['children']?.length) {
data['children'].push(res);
} else {
data['children'] = [res];
}
}
};
//替换成员
const replaceHandle = (node, parentNode) => {
// const data = parentNode ? parentNode['children'] : treeSource.value;
// const index = data.findIndex(item => item['accountId'] == node['accountId']);
let res = toDemo();
Object.assign(node, res);
// parentNode && (res['parentId'] = parentNode['accountId']);
// if (index != -1) {
// if (data[index]['children']) {
// res['children'] = data[index]['children'];
// }
// data.splice(index, 1, res);
// }
};
//删除成员
const delHandle = (node, parentNode) => {
const data = parentNode ? parentNode['children'] : treeSource.value;
const index = data.findIndex(item => item['accountId'] == node['accountId']);
data.splice(index, 1);
};
//动态加载
const onLoadData = data => {
return new Promise((resove, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
console.log('reject');
reject();
} else {
console.log('resove');
const { accountId, subTotalCount } = data;
const res = [];
for (let i = 0, len = subTotalCount; i < len; i++) {
const obj = toDemo();
obj['parentId'] = accountId;
obj[accountId] = uuid();
obj['fullname'] += i;
res.push(obj);
}
data.children = res;
resove();
}
}, 1e3);
});
};
const lookHandle = () => {
show.value = false;
nextTick(() => {
readonly.value = true;
treeSource.value = resLookData.data.pagedDatas;
show.value = true;
});
};
const uuid = () => {
return String(+new Date()) + Math.random();
};
const radom = (min, max) => {
return Math.round(Math.random() * max + min);
};
const toDemo = () => {
let data = {
parentId: '',
fullname: '小仙女' + radom(0, 1000),
accountId: radom(100, 10000),
avatar:
'https://p1.mingdaoyun.cn/UserAvatar/default9.png?watermark/2/text/5LuZ5aWz/font/5b6u6L2v6ZuF6buR/fontsize/700/fill/d2hpdGU=/dissolve/100/gravity/Center/dx/0/dy/0%7CimageView2/1/w/100/h/100/q/90',
department: '秘书办',
job: '董事会秘书',
status: 1,
subTotalCount: radom(0, 2),
hasSub: true
};
!data['subTotalCount'] && (data['hasSub'] = false);
return data;
};
</script>
<style lang="less" scoped>
.container {
height: 700px;
width: 100%;
position: relative;
background: #fff;
padding: 10px;
}
.ant-btn {
position: absolute;
top: 10px;
right: 10px;
}
</style>
@@ -0,0 +1,19 @@
<template>
<Card hoverable :style="{ width: '240px', background: '#fff' }">
<template #cover>
<img alt="example" src="https://os.alipayobjects.com/rmsportal/QBnOOoLaAfKPirc.png" />
</template>
<CardMeta title="懒加载组件" />
</Card>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Card } from 'ant-design-vue';
export default defineComponent({
components: { CardMeta: Card.Meta, Card },
setup() {
return {};
},
});
</script>
+77
View File
@@ -0,0 +1,77 @@
<template>
<PageWrapper title="懒加载自定义动画示例" content="懒加载组件显示动画">
<div class="lazy-base-demo-wrap">
<h1>向下滚动</h1>
<div class="lazy-base-demo-box">
<LazyContainer transitionName="custom">
<TargetContent />
</LazyContainer>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import TargetContent from './TargetContent.vue';
import { LazyContainer } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { LazyContainer, TargetContent, PageWrapper },
});
</script>
<style lang="less">
.lazy-base-demo {
&-wrap {
display: flex;
width: 50%;
height: 2000px;
margin: 20px auto;
text-align: center;
background-color: @component-background;
justify-content: center;
flex-direction: column;
align-items: center;
}
&-box {
width: 300px;
height: 300px;
}
h1 {
height: 1300px;
margin: 20px 0;
}
}
.custom-enter {
opacity: 0;
transform: scale(0.4) translate(100%);
}
.custom-enter-to {
opacity: 1;
}
.custom-enter-active {
position: absolute;
top: 0;
width: 100%;
transition: all 0.5s;
}
.custom-leave {
opacity: 1;
}
.custom-leave-to {
opacity: 0;
transform: scale(0.4) translate(-100%);
}
.custom-leave-active {
transition: all 0.5s;
}
</style>
+52
View File
@@ -0,0 +1,52 @@
<template>
<PageWrapper title="懒加载基础示例" content="向下滚动到可见区域才会加载组件">
<div class="lazy-base-demo-wrap">
<h1>向下滚动</h1>
<div class="lazy-base-demo-box">
<LazyContainer>
<TargetContent />
<template #skeleton>
<Skeleton :rows="10" />
</template>
</LazyContainer>
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Skeleton } from 'ant-design-vue';
import TargetContent from './TargetContent.vue';
import { LazyContainer } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { LazyContainer, PageWrapper, TargetContent, Skeleton },
});
</script>
<style lang="less">
.lazy-base-demo {
&-wrap {
display: flex;
width: 50%;
height: 2000px;
margin: 20px auto;
text-align: center;
background-color: @component-background;
justify-content: center;
flex-direction: column;
align-items: center;
}
&-box {
width: 300px;
height: 300px;
}
h1 {
height: 1300px;
margin: 20px 0;
}
}
</style>
+101
View File
@@ -0,0 +1,101 @@
<template>
<PageWrapper v-loading="loadingRef" loading-tip="加载中..." title="Loading组件示例">
<div ref="wrapEl">
<a-alert message="组件方式" />
<a-button class="my-4 mr-4" type="primary" @click="openCompFullLoading"> 全屏 Loading </a-button>
<a-button class="my-4" type="primary" @click="openCompAbsolute"> 容器内 Loading </a-button>
<Loading :loading="loading" :absolute="absolute" :theme="theme" :background="background" :tip="tip" />
<a-alert message="函数方式" />
<a-button class="my-4 mr-4" type="primary" @click="openFnFullLoading"> 全屏 Loading </a-button>
<a-button class="my-4" type="primary" @click="openFnWrapLoading"> 容器内 Loading </a-button>
<a-alert message="指令方式" />
<a-button class="my-4 mr-4" type="primary" @click="openDirectiveLoading"> 打开指令Loading </a-button>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs, ref } from 'vue';
import { Loading, useLoading } from '/@/components/Loading';
import { PageWrapper } from '/@/components/Page';
import { Alert } from 'ant-design-vue';
export default defineComponent({
components: { Loading, PageWrapper, [Alert.name]: Alert },
setup() {
const wrapEl = ref<ElRef>(null);
const loadingRef = ref(false);
const compState = reactive({
absolute: false,
loading: false,
theme: 'dark',
background: 'rgba(111,111,111,.7)',
tip: '加载中...',
});
const [openFullLoading, closeFullLoading] = useLoading({
tip: '加载中...',
});
const [openWrapLoading, closeWrapLoading] = useLoading({
target: wrapEl,
props: {
tip: '加载中...',
absolute: true,
},
});
function openLoading(absolute: boolean) {
compState.absolute = absolute;
compState.loading = true;
setTimeout(() => {
compState.loading = false;
}, 2000);
}
function openCompFullLoading() {
openLoading(false);
}
function openCompAbsolute() {
openLoading(true);
}
function openFnFullLoading() {
openFullLoading();
setTimeout(() => {
closeFullLoading();
}, 2000);
}
function openFnWrapLoading() {
openWrapLoading();
setTimeout(() => {
closeWrapLoading();
}, 2000);
}
function openDirectiveLoading() {
loadingRef.value = true;
setTimeout(() => {
loadingRef.value = false;
}, 2000);
}
return {
openCompFullLoading,
openFnFullLoading,
openFnWrapLoading,
openCompAbsolute,
wrapEl,
loadingRef,
openDirectiveLoading,
...toRefs(compState),
};
},
});
</script>
+58
View File
@@ -0,0 +1,58 @@
<template>
<BasicModal v-bind="$attrs" destroyOnClose @register="register" title="Modal Title" :helpMessage="['提示1', '提示2']" @visible-change="handleShow">
<template #insertFooter>
<a-button type="primary" danger @click="setLines" :disabled="loading">点我更新内容</a-button>
</template>
<template v-if="loading">
<div class="empty-tips"> 加载中稍等3秒 </div>
</template>
<template v-if="!loading">
<ul>
<li v-for="index in lines" :key="index">加载完成{{ index }}</li>
</ul>
</template>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent, ref, watch } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
export default defineComponent({
components: { BasicModal },
setup() {
const loading = ref(true);
const lines = ref(10);
const [register, { setModalProps, redoModalHeight }] = useModalInner();
watch(
() => lines.value,
() => {
redoModalHeight();
}
);
function handleShow(visible: boolean) {
if (visible) {
loading.value = true;
setModalProps({ loading: true, confirmLoading: true });
setTimeout(() => {
lines.value = Math.round(Math.random() * 30 + 5);
loading.value = false;
setModalProps({ loading: false, confirmLoading: false });
}, 3000);
}
}
function setLines() {
lines.value = Math.round(Math.random() * 20 + 10);
}
return { register, loading, handleShow, lines, setLines };
},
});
</script>
<style scoped>
.empty-tips {
height: 100px;
line-height: 100px;
text-align: center;
}
</style>
+23
View File
@@ -0,0 +1,23 @@
<template>
<BasicModal @register="register" title="Modal Title" :helpMessage="['提示1', '提示2']" :okButtonProps="{ disabled: true }">
<a-button type="primary" @click="closeModal" class="mr-2"> 从内部关闭弹窗 </a-button>
<a-button type="primary" @click="setModalProps"> 从内部修改title </a-button>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
export default defineComponent({
components: { BasicModal },
setup() {
const [register, { closeModal, setModalProps }] = useModalInner();
return {
register,
closeModal,
setModalProps: () => {
setModalProps({ title: 'Modal New Title' });
},
};
},
});
</script>
+15
View File
@@ -0,0 +1,15 @@
<template>
<BasicModal v-bind="$attrs" title="Modal Title" :helpMessage="['提示1', '提示2']" width="700px">
<p class="h-20" v-for="index in 20" :key="index"> 根据屏幕高度自适应 </p>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicModal } from '/@/components/Modal';
export default defineComponent({
components: { BasicModal },
setup() {
return {};
},
});
</script>
+81
View File
@@ -0,0 +1,81 @@
<template>
<BasicModal v-bind="$attrs" @register="register" title="Modal Title" @visible-change="handleVisibleChange">
<div class="pt-3px pr-3px">
<BasicForm @register="registerForm" :model="model" />
</div>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent, ref, nextTick } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Input',
label: '字段1',
colProps: {
span: 24,
},
defaultValue: '111',
},
{
field: 'field2',
component: 'Input',
label: '字段2',
colProps: {
span: 24,
},
},
];
export default defineComponent({
components: { BasicModal, BasicForm },
props: {
userData: { type: Object },
},
setup(props) {
const modelRef = ref({});
const [
registerForm,
{
// setFieldsValue,
// setProps
},
] = useForm({
labelWidth: 120,
schemas,
showActionButtonGroup: false,
actionColOptions: {
span: 24,
},
});
const [register] = useModalInner((data) => {
data && onDataReceive(data);
});
function onDataReceive(data) {
console.log('Data Received', data);
// 方式1;
// setFieldsValue({
// field2: data.data,
// field1: data.info,
// });
// // 方式2
modelRef.value = { field2: data.data, field1: data.info };
// setProps({
// model:{ field2: data.data, field1: data.info }
// })
}
function handleVisibleChange(v) {
v && props.userData && nextTick(() => onDataReceive(props.userData));
}
return { register, schemas, registerForm, model: modelRef, handleVisibleChange };
},
});
</script>
+112
View File
@@ -0,0 +1,112 @@
<template>
<PageWrapper title="modal组件使用示例">
<Alert
message="使用 useModal 进行弹窗操作,默认可以拖动,可以通过 draggable
参数进行控制是否可以拖动/全屏,并演示了在Modal内动态加载内容并自动调整高度"
show-icon
/>
<a-button type="primary" class="my-4" @click="openModalLoading"> 打开弹窗,加载动态数据并自动调整高度(默认可以拖动/全屏) </a-button>
<Alert message="内外同时同时显示隐藏" show-icon />
<a-button type="primary" class="my-4" @click="openModal2"> 打开弹窗</a-button>
<Alert message="自适应高度" show-icon />
<a-button type="primary" class="my-4" @click="openModal3"> 打开弹窗</a-button>
<Alert message="内外数据交互" show-icon />
<a-button type="primary" class="my-4" @click="send"> 打开弹窗并传递数据</a-button>
<Alert message="使用动态组件的方式在页面内使用多个弹窗" show-icon />
<a-space>
<a-button type="primary" class="my-4" @click="openTargetModal(1)"> 打开弹窗1</a-button>
<a-button type="primary" class="my-4" @click="openTargetModal(2)"> 打开弹窗2</a-button>
<a-button type="primary" class="my-4" @click="openTargetModal(3)"> 打开弹窗3</a-button>
<a-button type="primary" class="my-4" @click="openTargetModal(4)"> 打开弹窗4</a-button>
</a-space>
<component :is="currentModal" v-model:visible="modalVisible" :userData="userData" />
<Modal1 @register="register1" :minHeight="100" />
<Modal2 @register="register2" />
<Modal3 @register="register3" />
<Modal4 @register="register4" />
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, shallowRef, ComponentOptions, ref, nextTick } from 'vue';
import { Alert, Space } from 'ant-design-vue';
import { useModal } from '/@/components/Modal';
import Modal1 from './Modal1.vue';
import Modal2 from './Modal2.vue';
import Modal3 from './Modal3.vue';
import Modal4 from './Modal4.vue';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { Alert, Modal1, Modal2, Modal3, Modal4, PageWrapper, ASpace: Space },
setup() {
const currentModal = shallowRef<Nullable<ComponentOptions>>(null);
const [register1, { openModal: openModal1 }] = useModal();
const [register2, { openModal: openModal2 }] = useModal();
const [register3, { openModal: openModal3 }] = useModal();
const [register4, { openModal: openModal4 }] = useModal();
const modalVisible = ref<Boolean>(false);
const userData = ref<any>(null);
function send() {
openModal4(true, {
data: 'content',
info: 'Info',
});
}
function openModalLoading() {
openModal1(true);
// setModalProps({ loading: true });
// setTimeout(() => {
// setModalProps({ loading: false });
// }, 2000);
}
function openTargetModal(index) {
switch (index) {
case 1:
currentModal.value = Modal1;
break;
case 2:
currentModal.value = Modal2;
break;
case 3:
currentModal.value = Modal3;
break;
default:
currentModal.value = Modal4;
break;
}
nextTick(() => {
// `useModal` not working with dynamic component
// passing data through `userData` prop
userData.value = { data: Math.random(), info: 'Info222' };
// open the target modal
modalVisible.value = true;
});
}
return {
register1,
openModal1,
register2,
openModal2,
register3,
openModal3,
register4,
openModal4,
modalVisible,
userData,
openTargetModal,
send,
currentModal,
openModalLoading,
};
},
});
</script>
+117
View File
@@ -0,0 +1,117 @@
<template>
<PageWrapper title="二维码组件使用示例">
<div class="flex flex-wrap">
<CollapseContainer title="基础示例" :canExpan="true" class="text-center mb-6 qrcode-demo-item">
<QrCode :value="qrCodeUrl" />
</CollapseContainer>
<CollapseContainer title="渲染成img标签示例" class="text-center mb-6 qrcode-demo-item">
<QrCode :value="qrCodeUrl" tag="img" />
</CollapseContainer>
<CollapseContainer title="配置样式示例" class="text-center mb-6 qrcode-demo-item">
<QrCode
:value="qrCodeUrl"
:options="{
color: { dark: '#55D187' },
}"
/>
</CollapseContainer>
<CollapseContainer title="本地logo示例" class="text-center mb-6 qrcode-demo-item">
<QrCode :value="qrCodeUrl" :logo="LogoImg" />
</CollapseContainer>
<CollapseContainer title="在线logo示例" class="text-center mb-6 qrcode-demo-item">
<QrCode
:value="qrCodeUrl"
logo="http://jeecg.com/images/logo.png"
:options="{
color: { dark: '#55D187' },
}"
/>
</CollapseContainer>
<CollapseContainer title="logo配置示例" class="text-center mb-6 qrcode-demo-item">
<QrCode
:value="qrCodeUrl"
:logo="{
src: 'http://jeecg.com/images/logo.png',
logoSize: 0.2,
borderSize: 0.05,
borderRadius: 50,
bgColor: 'blue',
}"
/>
</CollapseContainer>
<CollapseContainer title="下载示例" class="text-center qrcode-demo-item">
<QrCode :value="qrCodeUrl" ref="qrRef" :logo="LogoImg" />
<a-button class="mb-2" type="primary" @click="download"> 下载 </a-button>
<div class="msg"> (在线logo会导致图片跨域需要下载图片需要自行解决跨域问题) </div>
</CollapseContainer>
<CollapseContainer title="配置大小示例" class="text-center qrcode-demo-item">
<QrCode :value="qrCodeUrl" :width="300" />
</CollapseContainer>
<CollapseContainer title="扩展绘制示例" class="text-center qrcode-demo-item">
<QrCode :value="qrCodeUrl" :width="200" :options="{ margin: 5 }" ref="qrDiyRef" :logo="LogoImg" @done="onQrcodeDone" />
<a-button class="mb-2" type="primary" @click="downloadDiy"> 下载 </a-button>
<div class="msg"> 要进行扩展绘制则不能将tag设为img </div>
</CollapseContainer>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import { QrCode, QrCodeActionType } from '/@/components/Qrcode/index';
import LogoImg from '/@/assets/images/logo.png';
import { CollapseContainer } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page';
const qrCodeUrl = 'https://www.vvbin.cn';
export default defineComponent({
components: { CollapseContainer, QrCode, PageWrapper },
setup() {
const qrRef = ref<Nullable<QrCodeActionType>>(null);
const qrDiyRef = ref<Nullable<QrCodeActionType>>(null);
function download() {
const qrEl = unref(qrRef);
if (!qrEl) return;
qrEl.download('文件名');
}
function downloadDiy() {
const qrEl = unref(qrDiyRef);
if (!qrEl) return;
qrEl.download('Qrcode');
}
function onQrcodeDone({ ctx }: any) {
if (ctx instanceof CanvasRenderingContext2D) {
// 额外绘制
ctx.fillStyle = 'black';
ctx.font = '16px "微软雅黑"';
ctx.textBaseline = 'bottom';
ctx.textAlign = 'center';
ctx.fillText('你帅你先扫', 100, 195, 200);
}
}
return {
onQrcodeDone,
qrCodeUrl,
LogoImg,
download,
downloadDiy,
qrRef,
qrDiyRef,
};
},
});
</script>
<style scoped>
.qrcode-demo-item {
width: 30%;
margin-right: 1%;
}
</style>
+59
View File
@@ -0,0 +1,59 @@
<template>
<PageWrapper title="滚动组件函数示例" content="基于el-scrollbar">
<div class="my-4">
<a-button @click="scrollTo(100)" class="mr-2"> 滚动到100px位置 </a-button>
<a-button @click="scrollTo(800)" class="mr-2"> 滚动到800px位置 </a-button>
<a-button @click="scrollTo(0)" class="mr-2"> 滚动到顶部 </a-button>
<a-button @click="scrollBottom()" class="mr-2"> 滚动到底部 </a-button>
</div>
<div class="scroll-wrap">
<ScrollContainer class="mt-4" ref="scrollRef">
<ul class="p-3">
<template v-for="index in 100" :key="index">
<li class="p-2" :style="{ border: '1px solid #eee' }">
{{ index }}
</li>
</template>
</ul>
</ScrollContainer>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import { ScrollContainer, ScrollActionType } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { ScrollContainer, PageWrapper },
setup() {
const scrollRef = ref<Nullable<ScrollActionType>>(null);
const getScroll = () => {
const scroll = unref(scrollRef);
if (!scroll) {
throw new Error('scroll is Null');
}
return scroll;
};
function scrollTo(top: number) {
getScroll().scrollTo(top);
}
function scrollBottom() {
getScroll().scrollBottom();
}
return {
scrollTo,
scrollRef,
scrollBottom,
};
},
});
</script>
<style lang="less" scoped>
.scroll-wrap {
width: 50%;
height: 300px;
background-color: @component-background;
}
</style>
@@ -0,0 +1,64 @@
<template>
<PageWrapper class="virtual-scroll-demo">
<Divider>基础滚动示例</Divider>
<div class="virtual-scroll-demo-wrap">
<VScroll :itemHeight="41" :items="data" :height="300" :width="300">
<template #default="{ item }">
<div class="virtual-scroll-demo__item">
{{ item.title }}
</div>
</template>
</VScroll>
</div>
<Divider>即使不可见也预先加载50条数据防止空白</Divider>
<div class="virtual-scroll-demo-wrap">
<VScroll :itemHeight="41" :items="data" :height="300" :width="300" :bench="50">
<template #default="{ item }">
<div class="virtual-scroll-demo__item">
{{ item.title }}
</div>
</template>
</VScroll>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { VScroll } from '/@/components/VirtualScroll/index';
import { Divider } from 'ant-design-vue';
import { PageWrapper } from '/@/components/Page';
const data: Recordable[] = (() => {
const arr: Recordable[] = [];
for (let index = 1; index < 20000; index++) {
arr.push({
title: '列表项' + index,
});
}
return arr;
})();
export default defineComponent({
components: { VScroll: VScroll, Divider, PageWrapper },
setup() {
return { data: data };
},
});
</script>
<style lang="less" scoped>
.virtual-scroll-demo {
&-wrap {
display: flex;
margin: 0 30%;
background-color: @component-background;
justify-content: center;
}
&__item {
height: 40px;
padding: 0 20px;
line-height: 40px;
border-bottom: 1px solid @border-color-base;
}
}
</style>
+31
View File
@@ -0,0 +1,31 @@
<template>
<PageWrapper title="滚动组件示例" content="基于el-scrollbar">
<div class="scroll-wrap">
<ScrollContainer class="mt-4">
<ul class="p-3">
<template v-for="index in 100" :key="index">
<li class="p-2" :style="{ border: '1px solid #eee' }">
{{ index }}
</li>
</template>
</ul>
</ScrollContainer>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { ScrollContainer } from '/@/components/Container/index';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { ScrollContainer, PageWrapper },
});
</script>
<style lang="less" scoped>
.scroll-wrap {
width: 50%;
height: 300px;
background-color: @component-background;
}
</style>
@@ -0,0 +1,32 @@
<template>
<PageWrapper title="密码强度校验组件">
<div class="flex justify-center">
<div class="demo-wrap p-10">
<StrengthMeter placeholder="默认" />
<StrengthMeter placeholder="禁用" disabled />
<br />
<StrengthMeter placeholder="隐藏input" :show-input="false" value="!@#qwe12345" />
</div>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { StrengthMeter } from '/@/components/StrengthMeter';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: {
StrengthMeter,
PageWrapper,
},
});
</script>
<style lang="less" scoped>
.demo-wrap {
width: 50%;
background-color: @component-background;
border-radius: 10px;
}
</style>
+44
View File
@@ -0,0 +1,44 @@
<template>
<PageWrapper title="时间组件示例">
<CollapseContainer title="基础示例">
<Time :value="time1" />
<br />
<Time :value="time2" />
</CollapseContainer>
<CollapseContainer title="定时更新" class="my-4">
<Time :value="now" :step="1" />
<br />
<Time :value="now" :step="5" />
</CollapseContainer>
<CollapseContainer title="定时更新">
<Time :value="now" mode="date" />
<br />
<Time :value="now" mode="datetime" />
<br />
<Time :value="now" />
</CollapseContainer>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { PageWrapper } from '/@/components/Page';
import { Time } from '/@/components/Time';
import { CollapseContainer } from '/@/components/Container/index';
export default defineComponent({
components: { PageWrapper, Time, CollapseContainer },
setup() {
const now = new Date().getTime();
const state = reactive({
time1: now - 60 * 3 * 1000,
time2: now - 86400 * 3 * 1000,
});
return {
...toRefs(state),
now,
};
},
});
</script>
+91
View File
@@ -0,0 +1,91 @@
<template>
<PageWrapper title="动画组件示例">
<div class="flex">
<Select :options="options" v-model:value="value" placeholder="选择动画" :style="{ width: '150px' }" />
<a-button type="primary" class="ml-4" @click="start"> start </a-button>
</div>
<component :is="`${value}Transition`">
<div class="box" v-show="show"></div>
</component>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { Select } from 'ant-design-vue';
import { PageWrapper } from '/@/components/Page';
import {
FadeTransition,
ScaleTransition,
SlideYTransition,
ScrollYTransition,
SlideYReverseTransition,
ScrollYReverseTransition,
SlideXTransition,
ScrollXTransition,
SlideXReverseTransition,
ScrollXReverseTransition,
ScaleRotateTransition,
ExpandXTransition,
ExpandTransition,
} from '/@/components/Transition';
const transitionList = [
'Fade',
'Scale',
'SlideY',
'ScrollY',
'SlideYReverse',
'ScrollYReverse',
'SlideX',
'ScrollX',
'SlideXReverse',
'ScrollXReverse',
'ScaleRotate',
'ExpandX',
'Expand',
];
const options = transitionList.map((item) => ({
label: item,
value: item,
key: item,
}));
export default defineComponent({
components: {
Select,
PageWrapper,
FadeTransition,
ScaleTransition,
SlideYTransition,
ScrollYTransition,
SlideYReverseTransition,
ScrollYReverseTransition,
SlideXTransition,
ScrollXTransition,
SlideXReverseTransition,
ScrollXReverseTransition,
ScaleRotateTransition,
ExpandXTransition,
ExpandTransition,
},
setup() {
const value = ref('Fade');
const show = ref(true);
function start() {
show.value = false;
setTimeout(() => {
show.value = true;
}, 300);
}
return { options, value, start, show };
},
});
</script>
<style lang="less" scoped>
.box {
width: 150px;
height: 150px;
margin-top: 20px;
background-color: rgb(126, 170, 236);
}
</style>
+54
View File
@@ -0,0 +1,54 @@
<template>
<PageWrapper title="上传组件示例">
<a-alert message="基础示例" />
<BasicUpload :maxSize="20" :maxNumber="10" @change="handleChange" :api="uploadApi" class="my-5" :accept="['image/*']" />
<a-alert message="嵌入表单,加入表单校验" />
<BasicForm @register="register" class="my-5" />
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicUpload } from '/@/components/Upload';
import { useMessage } from '/@/hooks/web/useMessage';
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
import { PageWrapper } from '/@/components/Page';
import { Alert } from 'ant-design-vue';
import { uploadApi } from '/@/api/sys/upload';
const schemas: FormSchema[] = [
{
field: 'field1',
component: 'Upload',
label: '字段1',
colProps: {
span: 8,
},
rules: [{ required: true, message: '请选择上传文件' }],
componentProps: {
api: uploadApi,
},
},
];
export default defineComponent({
components: { BasicUpload, BasicForm, PageWrapper, [Alert.name]: Alert },
setup() {
const { createMessage } = useMessage();
const [register] = useForm({
labelWidth: 120,
schemas,
actionColOptions: {
span: 16,
},
});
return {
handleChange: (list: string[]) => {
createMessage.info(`已上传文件${JSON.stringify(list)}`);
},
uploadApi,
register,
};
},
});
</script>
+33
View File
@@ -0,0 +1,33 @@
<template>
<PageWrapper title="旋转校验示例">
<div class="flex justify-center p-4 items-center bg-gray-700">
<RotateDragVerify :src="img" ref="el" @success="handleSuccess" />
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { RotateDragVerify } from '/@/components/Verify/index';
import img from '/@/assets/images/header.jpg';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { RotateDragVerify, PageWrapper },
setup() {
const handleSuccess = () => {
console.log('success!');
};
return {
handleSuccess,
img,
};
},
});
</script>
<style lang="less" scoped>
.bg-gray-700 {
background-color: #4a5568;
}
</style>
+97
View File
@@ -0,0 +1,97 @@
<template>
<PageWrapper title="拖动校验示例">
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el1" @success="handleSuccess" />
<a-button type="primary" class="ml-2" @click="handleBtnClick(el1)"> 还原 </a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el2" @success="handleSuccess" circle />
<a-button type="primary" class="ml-2" @click="handleBtnClick(el2)"> 还原 </a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify
ref="el3"
@success="handleSuccess"
text="拖动以进行校验"
successText="校验成功"
:barStyle="{
backgroundColor: '#018ffb',
}"
/>
<a-button type="primary" class="ml-2" @click="handleBtnClick(el3)"> 还原 </a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el4" @success="handleSuccess">
<template #actionIcon="isPassing">
<BugOutlined v-if="isPassing" />
<RightOutlined v-else />
</template>
</BasicDragVerify>
<a-button type="primary" class="ml-2" @click="handleBtnClick(el4)"> 还原 </a-button>
</div>
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el5" @success="handleSuccess">
<template #text="isPassing">
<div v-if="isPassing">
<BugOutlined />
成功
</div>
<div v-else>
拖动
<RightOutlined />
</div>
</template>
</BasicDragVerify>
<a-button type="primary" class="ml-2" @click="handleBtnClick(el5)"> 还原 </a-button>
</div>
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { BasicDragVerify, DragVerifyActionType, PassingData } from '/@/components/Verify/index';
import { useMessage } from '/@/hooks/web/useMessage';
import { BugOutlined, RightOutlined } from '@ant-design/icons-vue';
import { PageWrapper } from '/@/components/Page';
export default defineComponent({
components: { BasicDragVerify, BugOutlined, RightOutlined, PageWrapper },
setup() {
const { createMessage } = useMessage();
const el1 = ref<Nullable<DragVerifyActionType>>(null);
const el2 = ref<Nullable<DragVerifyActionType>>(null);
const el3 = ref<Nullable<DragVerifyActionType>>(null);
const el4 = ref<Nullable<DragVerifyActionType>>(null);
const el5 = ref<Nullable<DragVerifyActionType>>(null);
function handleSuccess(data: PassingData) {
const { time } = data;
createMessage.success(`校验成功,耗时${time}`);
}
function handleBtnClick(elRef: Nullable<DragVerifyActionType>) {
if (!elRef) {
return;
}
elRef.resume();
}
return {
handleSuccess,
el1,
el2,
el3,
el4,
el5,
handleBtnClick,
};
},
});
</script>
<style lang="less" scoped>
.bg-gray-700 {
background-color: #4a5568;
}
</style>
@@ -0,0 +1,81 @@
<!-- 标题与字段布局 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" @submit="handleSubmit" style="margin: 20px auto"/>
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
label: '姓名',
field: 'name',
component: 'Input',
},
{
label: '年龄',
field: 'password',
component: 'InputNumber',
},
{
label: '生日',
field: 'birthday',
component: 'DatePicker',
},
{
label: '头像',
field: 'avatar',
component: 'JImageUpload',
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
showActionButtonGroup: false,
actionColOptions: { span: 12 },
//控制标题宽度占比
labelCol: {
xs: 2,
sm: 2,
md: 2,
lg: 9,
xl: 3,
xxl: 2,
},
//控制组件宽度占比
wrapperCol: {
xs: 15,
sm: 14,
md: 16,
lg: 17,
xl: 19,
xxl: 20,
},
});
/**
* 点击提交按钮的value值
* @param values
*/
function handleSubmit(values: any) {
console.log('提交按钮数据::::', values);
}
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,70 @@
<!-- 固定label标题的宽度 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" @submit="handleSubmit" style="margin-top: 20px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
label: '姓名',
field: 'name',
component: 'Input',
},
{
label: '年龄',
field: 'password',
component: 'InputNumber',
},
{
label: '生日',
field: 'birthday',
component: 'DatePicker',
},
{
label: '头像',
field: 'avatar',
component: 'JImageUpload',
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
showResetButton: false,
submitButtonOptions: { text: '提交', preIcon: '' },
actionColOptions: { span: 17 },
//使用labelWidth控制标题宽度
labelWidth: '150px',
//使用labelCol的样式属性来控制标题宽度
labelCol: { style: { width: '150px' } },
//标题对齐方式(left:左对齐,right:右对齐),默认右对齐
labelAlign: 'right',
});
/**
* 点击提交按钮的value值
* @param values
*/
function handleSubmit(values: any) {
console.log('提交按钮数据::::', values);
}
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,143 @@
<!-- 动态增减表单 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px;" @submit="handleSubmit">
<!-- 添加input的插槽 -->
<template #addForm="{ field }">
<a-button v-if="Number(field) === 0" @click="addField" style="width: 50px">+</a-button>
<a-button v-if="Number(field) > 0" @click="delField(field)" style="width: 50px">-</a-button>
</template>
</BasicForm>
<!-- <div style="margin: 20px auto; text-align: center">
<a-button @click="addField">添加表单项</a-button>
</div>-->
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { CollapseContainer } from '/@/components/Container';
import { ref } from 'vue';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'name1',
label: '姓名1',
component: 'Input',
// ifShow:false,
colProps: {
span: 8,
},
},
{
field: 'age1',
label: '年龄1',
component: 'InputNumber',
// ifShow:false,
colProps: {
span: 8,
},
},
{
field: '0',
component: 'Input',
// ifShow:false,
label: ' ',
colProps: {
span: 8,
},
slot: 'addForm',
},
];
/**
* BasicForm绑定注册;
* appendSchemaByField:增加表单项(字段)
*
* removeSchemaByFiled:减少表单项(字段)
*/
const [registerForm, { appendSchemaByField, removeSchemaByFiled }] = useForm({
schemas: formSchemas,
showResetButton: false,
labelWidth: '150px',
// showSubmitButton:false
submitButtonOptions: { text: '提交', preIcon: '' },
actionColOptions: { span: 17 },
});
//组件个数
let n = ref<number>(2);
/**
* 添加字段
* appendSchemaByField类型: ( schema: FormSchema, prefixField: string | undefined, first?: boolean | undefined ) => Promise<void>
* 说明: 插入到指定 filed 后面,如果没传指定 field,则插入到最后,当 first = true 时插入到第一个位置
*/
async function addField() {
//添加表单字段,里面为schemas对应的属性,可自行配置
await appendSchemaByField(
{
field: `name${n.value}`,
component: 'Input',
label: '字段' + n.value,
colProps: {
span: 8,
},
},
''
);
await appendSchemaByField(
{
field: `sex${n.value}`,
component: 'InputNumber',
label: '字段' + n.value,
colProps: {
span: 8,
},
},
''
);
await appendSchemaByField(
{
field: `${n.value}`,
component: 'Input',
label: ' ',
colProps: {
span: 8,
},
slot: 'addForm',
},
''
);
n.value++;
}
/**
* 删除字段
* 类型: (field: string | string[]) => Promise<void>
* 说明: 根据 field 删除 Schema
* @param field 当前字段名称
*/
function delField(field) {
//移除指定字段
removeSchemaByFiled([`name${field}`, `sex${field}`, `${field}`]);
n.value--;
}
/**
* 点击提交按钮的value值
* @param values
*/
function handleSubmit(values: any) {
console.log('提交按钮数据::::', values);
}
</script>
<style scoped>
/** 数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
</style>
@@ -0,0 +1,66 @@
<!-- 操作按钮 -->
<template>
<div style="margin: 20px auto; text-align: center">
<!-- 通过setProps 可以设置 userForm 中的属性 -->
<!-- showActionButtonGroup 显示或者隐藏查询重置按钮 -->
<a-button @click="setProps({ showActionButtonGroup: false })" class="mr-2"> 隐藏操作按钮 </a-button>
<a-button @click="setProps({ showActionButtonGroup: true })" class="mr-2"> 显示操作按钮 </a-button>
<!-- showActionButtonGroup 显示或者隐藏重置按钮 -->
<a-button @click="setProps({ showResetButton: false })" class="mr-2"> 隐藏重置按钮 </a-button>
<a-button @click="setProps({ showResetButton: true })" class="mr-2"> 显示重置按钮 </a-button>
<!-- showActionButtonGroup 显示或者隐藏查询按钮 -->
<a-button @click="setProps({ showSubmitButton: false })" class="mr-2"> 隐藏查询按钮 </a-button>
<a-button @click="setProps({ showSubmitButton: true })" class="mr-2"> 显示查询按钮 </a-button>
</div>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" @submit="handleSubmit" style="margin-top: 50px; margin-left: 50px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { CollapseContainer } from '/@/components/Container';
/**
* BasicForm绑定注册;
* setProps方法可以动态设置useForm中的属性
*/
const [registerForm, { setProps }] = useForm({
//自定义查询按钮的文本和图标
submitButtonOptions: { text: '查询', preIcon: '' },
//自定义重置按钮的文本和图标
resetButtonOptions: { text: '重置', preIcon: '' },
//操作按钮的位置
actionColOptions: { span: 17 },
//提交按钮的自定义事件
submitFunc: customSubmitFunc,
//重置按钮的自定义时间
resetFunc: customSubmitFunc,
//显示操作按钮
showActionButtonGroup: true,
});
/**
* 查询按钮点击事件
*/
async function customSubmitFunc() {
console.log('查询按钮点击事件,此处处理查询按钮的逻辑');
}
/**
* 重置按钮点击事件
*/
async function customResetFunc() {
console.log('重置按钮点击事件,此处处理重置按钮的逻辑');
}
/**
* 点击提交按钮的value值
* @param values
*/
function handleSubmit(values: any) {
console.log('提交按钮数据::::', values);
}
</script>
<style scoped></style>
@@ -0,0 +1,95 @@
<!-- 操作禁用表单 -->
<template>
<div style="margin: 20px auto; text-align: center">
<!-- all 触发或清空所有验证visitor 只触发或者清空来访人员验证 -->
<a-button @click="triggerFormRule('all')" class="mr-2"> 触发表单验证 </a-button>
<a-button @click="cancelFormRule('all')" class="mr-2"> 清空表单验证 </a-button>
<a-button @click="triggerFormRule('visitor')" class="mr-2"> 只验证来访人员 </a-button>
<a-button @click="cancelFormRule('visitor')" class="mr-2"> 只清空来访人员验证 </a-button>
</div>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px;" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'visitor',
label: '来访人员',
component: 'Input',
required: true,
},
{
field: 'accessed',
label: '来访日期',
component: 'DatePicker',
required: true,
},
{
field: 'phone',
label: '来访人手机号',
component: 'Input',
required: true,
},
];
/**
* BasicForm绑定注册;
* clearValidate 清除所有验证,支持取消验证其中几个字段 如 clearValidate(['visitor',...])
* validate 验证所有,支持验证其中几个字段,validate(['visitor',...])
* validateFields 只支持验证其中几个字段,如validateFields(['visitor',...])
*/
const [registerForm, { clearValidate, validateFields, validate }] = useForm({
schemas: formSchemas,
labelWidth: '150px',
//隐藏操作按钮
showActionButtonGroup: false,
//默认聚焦第一个,只支持input
autoFocusFirstItem: true,
});
/**
* 触发表单验证
* @param type all 所有验证 visitor 只验证来访人员
*/
async function triggerFormRule(type) {
if (type == 'all') {
//触发所有验证
await validate();
} else {
//触发来访人员验证
//visitor 来访人员的对应formSchemas field字段
await validateFields(['visitor']);
}
}
/**
* 触发表单验证
* @param type all 所有验证 visitor 只验证来访人员
*/
async function cancelFormRule(type) {
if (type == 'all') {
//取消全部验证
await clearValidate();
} else {
//只取消来访人员的验证
//visitor 来访人员的对应formSchemas field字段
await clearValidate(['visitor']);
}
}
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,58 @@
<!-- 操作禁用表单 -->
<template>
<div style="margin: 20px auto; text-align: center">
<!-- 通过setProps 可以设置 userForm 中的属性 -->
<!-- 表单大小默认为 default -->
<a-button @click="setProps({ size: 'large' })" class="mr-2"> 更改大小为最大 </a-button>
<a-button @click="setProps({ size: 'default' })" class="mr-2"> 还原大小 </a-button>
<a-button @click="setProps({ size: 'small' })" class="mr-2"> 更改大小为最小 </a-button>
<!-- disabled表单禁用 -->
<a-button @click="setProps({ disabled: true })" class="mr-2"> 禁用表单 </a-button>
<a-button @click="setProps({ disabled: false })" class="mr-2"> 解除禁用 </a-button>
<!-- compact 是否为紧凑表单 -->
<a-button @click="setProps({ compact: true })" class="mr-2"> 紧凑表单 </a-button>
<a-button @click="setProps({ compact: false })" class="mr-2"> 还原正常间距 </a-button>
</div>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px;" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { CollapseContainer } from '/@/components/Container';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'visitor',
label: '来访人员',
component: 'Input',
},
{
field: 'accessed',
label: '来访日期',
component: 'DatePicker',
},
{
field: 'phone',
label: '来访人手机号',
component: 'Input',
},
];
/**
* BasicForm绑定注册;
* setProps方法可以动态设置useForm中的属性
*/
const [registerForm, { setProps }] = useForm({
schemas: formSchemas,
labelWidth: '150px',
//隐藏操作按钮
showActionButtonGroup: false,
//默认聚焦第一个,只支持input
autoFocusFirstItem: true,
});
</script>
<style scoped></style>
@@ -0,0 +1,34 @@
<!-- 操作表单值 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px;" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { schemas } from './example.data';
/**
* BasicForm绑定注册;
*/
const [registerForm, { getFieldsValue, setFieldsValue, resetFields, validate }] = useForm({
schemas: schemas,
labelWidth: '150px',
//隐藏操作按钮
showActionButtonGroup: false,
//默认聚焦第一个,只支持input
autoFocusFirstItem: true,
});
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,63 @@
<!-- 控件属性 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
label: '员工姓名',
field: 'name',
component: 'Input',
componentProps: {
disabled: true,
},
defaultValue: '张三',
},
{
label: '性别',
field: 'sex',
component: 'Select',
//填写组件的属性
componentProps: {
options: [
{ label: '男', value: 1 },
{ label: '女', value: 2 },
{ label: '未知', value: 3 },
],
},
//默认值
defaultValue: 3,
},
{
label: '年龄',
field: 'age',
component: 'Input',
},
{
label: '入职时间',
subLabel: '( 选填 )',
field: 'entryTime',
component: 'TimePicker',
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
labelWidth: '150px',
showResetButton: false,
submitButtonOptions: { text: '提交', preIcon: '' },
actionColOptions: { span: 17 },
});
</script>
<style scoped></style>
@@ -0,0 +1,32 @@
<!-- 自定义组件 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'custom',
label: '自定义组件',
//引入自定义组件
component: 'JInput',
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
labelWidth: '150px',
showActionButtonGroup: false,
});
</script>
<style scoped></style>
@@ -0,0 +1,32 @@
<!-- 操作表单值 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { schemas } from './exampleCustom.data';
/**
* BasicForm绑定注册;
*/
const [registerForm, { getFieldsValue, setFieldsValue, resetFields, validate }] = useForm({
schemas: schemas,
labelWidth: '150px',
//隐藏操作按钮
showActionButtonGroup: false,
});
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,64 @@
<!-- 插槽 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" @submit="handleSubmit">
<!-- #name对应的是formSchemas对应slot(name)插槽 -->
<template #name="{ model, field }">
<JInput v-model:value="model[field]" />
</template>
</BasicForm>
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//引入CustomDemo自定义组件
import JInput from "/@/components/Form/src/jeecg/components/JInput.vue";
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'name',
label: '姓名',
component: 'Input',
slot:'name'
},
{
field: 'phone',
label: '联系方式',
component: 'Input',
},
{
field: 'feedback',
label: '问题反馈',
component: 'InputTextArea',
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
showResetButton: false,
labelWidth: '150px',
submitButtonOptions: { text: '提交', preIcon: '' },
actionColOptions: { span: 17 },
});
/**
* 提交信息
*/
function handleSubmit(values) {
console.log("values::",values);
}
</script>
<style scoped>
.font-color {
font-size: 13px;
color: #a1a1a1;
margin-bottom: 5px;
}
</style>
@@ -0,0 +1,80 @@
<!-- 动态表单验证 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" @submit="handleSubmit" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { duplicateCheck } from '/@/views/system/user/user.api';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'visitor',
label: '来访人员',
component: 'Input',
//自动触发检验,布尔类型
required: true,
},
{
field: 'accessed',
label: '来访日期',
component: 'DatePicker',
//支持获取当前值判断触发 values代表当前表单的值
required: ({ values }) => {
return !values.accessed;
},
},
{
field: 'phone',
label: '来访人手机号',
component: 'Input',
//动态自定义正则,values: 当前表单的所有值
dynamicRules: ({ values }) => {
//需要return
return [
{
//默认开启表单检验
required: true,
// value 当前手机号输入的值
validator: (_, value) => {
//需要return 一个Promise对象
return new Promise((resolve, reject) => {
if (!value) {
reject('请输入手机号!');
}
//验证手机号是否正确
let reg = /^1[3456789]\d{9}$/;
if (!reg.test(value)) {
reject('请输入正确手机号!');
}
resolve();
});
},
},
];
},
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
showResetButton: false,
labelWidth: '150px',
submitButtonOptions: { text: '提交', preIcon: '' },
actionColOptions: { span: 17 },
});
/**
* 提交事件
*/
function handleSubmit(values: any) {}
</script>
<style scoped></style>
@@ -0,0 +1,70 @@
<!-- 字段显示与隐藏 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'id',
label: '编号',
component: 'Input',
//隐藏id,css 控制,不会删除 dom(支持布尔类型 true和false。支持动态值判断,详情请见ifShow)
show: false,
},
{
field: 'evaluate',
label: '对公司整体评价',
component: 'RadioGroup',
componentProps: {
options: [
{ label: '满意', value: '0' },
{ label: '不满意', value: '1' },
],
},
defaultValue: '0',
},
{
field: 'describe',
label: '不满意原因说明',
component: 'InputTextArea',
//ifShow和show属性一致,使用其中一个即可,values代表当前表单的值,js 控制,会删除 dom
ifShow: ({ values }) => {
return values.evaluate == '1';
},
},
{
field: 'satisfiedLevel',
label: '满意度',
component: 'Slider',
componentProps: {
tipFormatter: (value) => {
return value + '%';
},
},
//动态禁用,values代表当前表单的值,返回 true或false
dynamicDisabled: ({ values }) => {
return values.evaluate == '1';
},
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
showResetButton: false,
labelWidth: '150px',
submitButtonOptions: { text: '提交', preIcon: '' },
actionColOptions: { span: 17 },
});
</script>
<style scoped></style>
@@ -0,0 +1,55 @@
<!-- 字段标题提示及前缀 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
field: 'month',
label: '当前月份',
component: 'Input',
suffix: '月',
},
{
field: 'lateNumber',
label: '迟到次数',
component: 'InputNumber',
//帮助信息:可以直接返回String(helpMessage:"迟到次数"),也可以获取表单值,动态填写
helpMessage: ({ values }) => {
return '当前迟到次数' + values.lateNumber + ', 扣款' + values.lateNumber * 50 + '元';
},
defaultValue: 0,
},
{
field: 'lateReason',
label: '迟到原因',
component: 'Input',
helpMessage: '什么原因耽误上班迟到',
//自定义提示属性,需要结合helpMessage一起使用
helpComponentProps: {
maxWidth: '200px',
color: '#66CCFF',
},
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//注册表单列
schemas: formSchemas,
showResetButton: false,
labelWidth: '150px',
submitButtonOptions: { text: '提交', preIcon: '' },
actionColOptions: { span: 17 },
});
</script>
<style scoped></style>
@@ -0,0 +1,105 @@
<!-- 自定义页脚 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px">
<template #formHeader>
<div style="margin: 0 auto 20px">
<span>我是自定义按钮</span>
</div>
</template>
<template #formFooter>
<div style="margin: 0 auto">
<a-button type="primary" @click="save" class="mr-2"> 保存 </a-button>
<a-button type="primary" @click="saveDraft" class="mr-2"> 保存草稿 </a-button>
<a-button type="error" @click="reset" class="mr-2"> 重置 </a-button>
</div>
</template>
</BasicForm>
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
label: '员工姓名',
field: 'name',
component: 'Input',
},
{
label: '性别',
field: 'sex',
component: 'Select',
//填写组件的属性
componentProps: {
options: [
{ label: '男', value: 1 },
{ label: '女', value: 2 },
{ label: '未知', value: 3 },
],
},
//默认值
defaultValue: 3,
},
{
label: '年龄',
field: 'age',
component: 'Input',
},
{
label: '入职时间',
subLabel: '( 选填 )',
field: 'entryTime',
component: 'TimePicker',
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm, { validate, resetFields }] = useForm({
schemas: formSchemas,
labelWidth: '150px',
//隐藏操作按钮
showActionButtonGroup: false,
});
/**
* 保存
*/
async function save() {
//使用useForm方法获取表单值
let values = await validate();
console.log(values);
}
/**
* 保存草稿
*/
async function saveDraft() {
//使用useForm方法validate获取表单值
let values = await validate();
console.log(values);
}
/**
* 重置
*/
async function reset() {
//使用useForm方法resetFields清空值
await resetFields();
}
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,63 @@
<!-- 表单布局 -->
<template>
<!-- 自定义表单 -->
<BasicForm @register="registerForm" style="margin-top: 20px" />
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
label: '会议名称',
field: 'name',
component: 'Input',
},
{
label: '参会地点',
field: 'meetingLocation',
component: 'Input',
},
{
label: '参与人数',
field: 'numberOfPart',
component: 'InputNumber',
},
{
label: '会议纪要',
field: 'meetingMinutes',
component: 'JUpload',
},
];
/**
* BasicForm绑定注册;
*/
const [registerForm] = useForm({
//表单布局属性,支持(vertical,inline),默认为inline
layout: 'inline',
//注册表单列
schemas: formSchemas,
//不显示查询和重置按钮
showActionButtonGroup: false,
//默认row行配置,当 layout 为 inline 生效
rowProps: { gutter: 24, justify: 'center', align: 'middle' },
//全局col列占比(每列显示多少位),和schemas中的colProps属性一致
baseColProps: { span: 12 },
//row行的样式
baseRowStyle: { width: '100%' },
});
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>
@@ -0,0 +1,84 @@
<!-- 弹出层表单 -->
<template>
<div style="margin: 20px auto">
<a-button type="primary" @click="openPopup" class="mr-2"> 打开弹窗 </a-button>
</div>
<!-- 自定义弹窗组件 -->
<BasicModal @register="registerModal" title="弹出层表单">
<!-- 自定义表单 -->
<BasicForm @register="registerForm" />
</BasicModal>
</template>
<script lang="ts" setup>
//引入依赖
import { useForm, BasicForm, FormSchema } from '/@/components/Form';
import { BasicModal } from '/@/components/Modal';
import { useModal } from '/@/components/Modal';
//自定义表单字段
const formSchemas: FormSchema[] = [
{
label: '员工姓名',
field: 'name',
component: 'Input',
},
{
label: '性别',
field: 'sex',
component: 'Select',
//填写组件的属性
componentProps: {
options: [
{ label: '男', value: 1 },
{ label: '女', value: 2 },
{ label: '未知', value: 3 },
],
},
//默认值
defaultValue: 3,
},
{
label: '年龄',
field: 'age',
component: 'Input',
},
{
label: '入职时间',
subLabel: '( 选填 )',
field: 'entryTime',
component: 'TimePicker',
},
];
//BasicModal绑定注册;
const [registerModal, { openModal }] = useModal();
/**
* BasicForm绑定注册;
*/
const [registerForm, { validate, resetFields }] = useForm({
schemas: formSchemas,
//隐藏操作按钮
showActionButtonGroup: false,
});
/**
* 打开弹窗
*/
async function openPopup() {
//详见 BasicModal模块
openModal(true, {});
}
</script>
<style scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-picker) {
width: 100%;
}
</style>

Some files were not shown because too many files have changed in this diff Show More