mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-27 14:52:56 +00:00
docs(backend): 重构后端README文档,完善项目文档与开发指南
- 全面更新README,添加项目特性、架构设计与技术栈介绍 - 详细补充项目结构说明及模块设计规范 - 增加快速开始步骤,包括环境配置、数据库初始化和服务启动 - 补充主要API模块路径和认证授权使用示例 - 添加开发指南、数据库迁移与测试方法 - 集成监控、日志级别说明及性能监控内容 - 完善Docker、传统部署及Nginx配置示例 - 添加贡献指南和代码规范说明 - 新增MCP模块概述及智能对话API接口文档 - 清理和移除module_ai中旧的mcp_server相关实现代码 - 在api/v1初始化文件中注册AI模块路由 - 修改resource模块,增强资源路径安全检查和文件类型检测逻辑 - 增强资源搜索和上传服务的健壮性及安全性
This commit is contained in:
@@ -1,11 +1,5 @@
|
||||
<template>
|
||||
<wd-calendar
|
||||
v-model="dateRange"
|
||||
:label="label"
|
||||
type="daterange"
|
||||
:placeholder="placeholder"
|
||||
@confirm="handleConfirm"
|
||||
/>
|
||||
<wd-calendar v-model="dateRange" :label="label" type="daterange" :placeholder="placeholder" @confirm="handleConfirm" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -38,12 +32,7 @@ watch(
|
||||
} else if (typeof val === "string" && val.includes(",")) {
|
||||
const [startDate, endDate] = val.split(",");
|
||||
|
||||
if (
|
||||
startDate &&
|
||||
endDate &&
|
||||
!isNaN(new Date(startDate).getTime()) &&
|
||||
!isNaN(new Date(endDate).getTime())
|
||||
) {
|
||||
if (startDate && endDate && !isNaN(new Date(startDate).getTime()) && !isNaN(new Date(endDate).getTime())) {
|
||||
dateRange.value = [new Date(startDate).getTime(), new Date(endDate).getTime()];
|
||||
} else {
|
||||
dateRange.value = null;
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
<template>
|
||||
<wd-picker
|
||||
v-model="selectedValues"
|
||||
:columns="pickerColumns"
|
||||
:display-format="displayFormat"
|
||||
:column-change="handleColumnChange"
|
||||
:label="label"
|
||||
:placeholder="pickerColumns.length ? '请选择' : '加载中...'"
|
||||
:required="required"
|
||||
/>
|
||||
<wd-picker v-model="selectedValues" :columns="pickerColumns" :display-format="displayFormat" :column-change="handleColumnChange" :label="label" :placeholder="pickerColumns.length ? '请选择' : '加载中...'" :required="required" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -80,9 +72,7 @@ const findTreePath = (value: number | string): number[] | string[] => {
|
||||
* @param treeData 树形数据
|
||||
* @returns Picker 所需的 columns 格式
|
||||
*/
|
||||
const transformTreeToColumns = (
|
||||
treeData: OptionType[]
|
||||
): Array<Array<{ label: string; value: string | number }>> => {
|
||||
const transformTreeToColumns = (treeData: OptionType[]): Array<Array<{ label: string; value: string | number }>> => {
|
||||
const columns: Array<Array<{ label: string; value: string | number }>> = [];
|
||||
|
||||
for (let depth = 0; depth <= selectedValues.value.length; depth++) {
|
||||
@@ -117,12 +107,7 @@ watch(
|
||||
/**
|
||||
* 处理列的变化,动态更新后续列的数据
|
||||
*/
|
||||
function handleColumnChange(
|
||||
pickerView: PickerViewInstance,
|
||||
value: Record<string, any> | Record<string, any>[],
|
||||
columnIndex: number,
|
||||
resolve: () => void
|
||||
) {
|
||||
function handleColumnChange(pickerView: PickerViewInstance, value: Record<string, any> | Record<string, any>[], columnIndex: number, resolve: () => void) {
|
||||
const selectedValue = selectedValues.value[selectedValues.value.length - 1] || undefined;
|
||||
emits("update:modelValue", selectedValue);
|
||||
|
||||
@@ -134,11 +119,7 @@ function handleColumnChange(
|
||||
/**
|
||||
* 动态更新所有后续列的数据
|
||||
*/
|
||||
function updatePickerColumns(
|
||||
pickerView: PickerViewInstance,
|
||||
parentId: string | number,
|
||||
columnIndex: number
|
||||
) {
|
||||
function updatePickerColumns(pickerView: PickerViewInstance, parentId: string | number, columnIndex: number) {
|
||||
const nextColumnIndex = columnIndex + 1;
|
||||
const children = findChildren(parentId, props.data);
|
||||
|
||||
@@ -151,10 +132,7 @@ function updatePickerColumns(
|
||||
/**
|
||||
* 根据节点value查找其子节点数据
|
||||
*/
|
||||
function findChildren(
|
||||
parentId: string | number,
|
||||
list: Record<string, any>[]
|
||||
): Record<string, any>[] {
|
||||
function findChildren(parentId: string | number, list: Record<string, any>[]): Record<string, any>[] {
|
||||
for (const item of list) {
|
||||
if (item.value === parentId && item.children) {
|
||||
return item.children;
|
||||
|
||||
@@ -28,17 +28,7 @@
|
||||
<!-- APP和H5采用renderjs渲染图表 -->
|
||||
<!-- #ifdef APP-VUE || H5 -->
|
||||
<block v-if="echarts">
|
||||
<view
|
||||
v-show="showchart"
|
||||
:id="'EC' + cid"
|
||||
:style="{ background: background }"
|
||||
style="width: 100%; height: 100%"
|
||||
:data-directory="directory"
|
||||
:prop="echartsOpts"
|
||||
:change:prop="rdcharts.ecinit"
|
||||
:resize="echartsResize"
|
||||
:change:resize="rdcharts.ecresize"
|
||||
/>
|
||||
<view v-show="showchart" :id="'EC' + cid" :style="{ background: background }" style="width: 100%; height: 100%" :data-directory="directory" :prop="echartsOpts" :change:prop="rdcharts.ecinit" :resize="echartsResize" :change:resize="rdcharts.ecresize" />
|
||||
</block>
|
||||
<block v-else>
|
||||
<view
|
||||
@@ -53,14 +43,7 @@
|
||||
v-on:touchmove="rdcharts.touchMove"
|
||||
v-on:touchend="rdcharts.touchEnd"
|
||||
>
|
||||
<canvas
|
||||
v-show="showchart"
|
||||
:id="cid"
|
||||
:canvasId="cid"
|
||||
:style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }"
|
||||
:disable-scroll="disableScroll"
|
||||
@error="_error"
|
||||
/>
|
||||
<canvas v-show="showchart" :id="cid" :canvasId="cid" :style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }" :disable-scroll="disableScroll" @error="_error" />
|
||||
</view>
|
||||
</block>
|
||||
<!-- #endif -->
|
||||
@@ -83,72 +66,25 @@
|
||||
/>
|
||||
</block>
|
||||
<block v-if="!ontouch">
|
||||
<canvas
|
||||
v-show="showchart"
|
||||
:id="cid"
|
||||
:canvasId="cid"
|
||||
:width="cWidth * pixel"
|
||||
:height="cHeight * pixel"
|
||||
:style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }"
|
||||
:disable-scroll="disScroll"
|
||||
@tap="_tap"
|
||||
@error="_error"
|
||||
/>
|
||||
<canvas v-show="showchart" :id="cid" :canvasId="cid" :width="cWidth * pixel" :height="cHeight * pixel" :style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }" :disable-scroll="disScroll" @tap="_tap" @error="_error" />
|
||||
</block>
|
||||
<!-- #endif -->
|
||||
<!-- 其他小程序通过vue渲染图表 -->
|
||||
<!-- #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO || MP-KUAISHOU || MP-LARK || MP-JD || MP-360 -->
|
||||
<block v-if="type2d">
|
||||
<view v-if="ontouch" @tap="_tap">
|
||||
<canvas
|
||||
v-show="showchart"
|
||||
:id="cid"
|
||||
:canvasId="cid"
|
||||
:style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }"
|
||||
type="2d"
|
||||
:disable-scroll="disScroll"
|
||||
@touchstart="_touchStart"
|
||||
@touchmove="_touchMove"
|
||||
@touchend="_touchEnd"
|
||||
@error="_error"
|
||||
/>
|
||||
<canvas v-show="showchart" :id="cid" :canvasId="cid" :style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }" type="2d" :disable-scroll="disScroll" @touchstart="_touchStart" @touchmove="_touchMove" @touchend="_touchEnd" @error="_error" />
|
||||
</view>
|
||||
<view v-if="!ontouch" @tap="_tap">
|
||||
<canvas
|
||||
v-show="showchart"
|
||||
:id="cid"
|
||||
:canvasId="cid"
|
||||
:style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }"
|
||||
type="2d"
|
||||
:disable-scroll="disScroll"
|
||||
@error="_error"
|
||||
/>
|
||||
<canvas v-show="showchart" :id="cid" :canvasId="cid" :style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }" type="2d" :disable-scroll="disScroll" @error="_error" />
|
||||
</view>
|
||||
</block>
|
||||
<block v-if="!type2d">
|
||||
<view v-if="ontouch" @tap="_tap">
|
||||
<canvas
|
||||
v-if="showchart"
|
||||
:id="cid"
|
||||
:canvasId="cid"
|
||||
:style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }"
|
||||
:disable-scroll="disScroll"
|
||||
@touchstart="_touchStart"
|
||||
@touchmove="_touchMove"
|
||||
@touchend="_touchEnd"
|
||||
@error="_error"
|
||||
/>
|
||||
<canvas v-if="showchart" :id="cid" :canvasId="cid" :style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }" :disable-scroll="disScroll" @touchstart="_touchStart" @touchmove="_touchMove" @touchend="_touchEnd" @error="_error" />
|
||||
</view>
|
||||
<view v-if="!ontouch">
|
||||
<canvas
|
||||
v-if="showchart"
|
||||
:id="cid"
|
||||
:canvasId="cid"
|
||||
:style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }"
|
||||
:disable-scroll="disScroll"
|
||||
@tap="_tap"
|
||||
@error="_error"
|
||||
/>
|
||||
<canvas v-if="showchart" :id="cid" :canvasId="cid" :style="{ width: cWidth + 'px', height: cHeight + 'px', background: background }" :disable-scroll="disScroll" @tap="_tap" @error="_error" />
|
||||
</view>
|
||||
</block>
|
||||
<!-- #endif -->
|
||||
@@ -166,10 +102,7 @@ function deepCloneAssign(origin = {}, ...args) {
|
||||
for (let i in args) {
|
||||
for (let key in args[i]) {
|
||||
if (args[i].hasOwnProperty(key)) {
|
||||
origin[key] =
|
||||
args[i][key] && typeof args[i][key] === "object"
|
||||
? deepCloneAssign(Array.isArray(args[i][key]) ? [] : {}, origin[key], args[i][key])
|
||||
: args[i][key];
|
||||
origin[key] = args[i][key] && typeof args[i][key] === "object" ? deepCloneAssign(Array.isArray(args[i][key]) ? [] : {}, origin[key], args[i][key]) : args[i][key];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,11 +407,7 @@ export default {
|
||||
optsProps: {
|
||||
handler(val, oldval) {
|
||||
if (typeof val === "object") {
|
||||
if (
|
||||
JSON.stringify(val) !== JSON.stringify(oldval) &&
|
||||
this.echarts === false &&
|
||||
this.optsWatch == true
|
||||
) {
|
||||
if (JSON.stringify(val) !== JSON.stringify(oldval) && this.echarts === false && this.optsWatch == true) {
|
||||
this.checkData(this.drawData);
|
||||
}
|
||||
} else {
|
||||
@@ -562,11 +491,7 @@ export default {
|
||||
}
|
||||
// #ifdef MP-WEIXIN
|
||||
this.inWx = true;
|
||||
if (
|
||||
this.canvas2d === false ||
|
||||
systemInfo.platform === "windows" ||
|
||||
systemInfo.platform === "mac"
|
||||
) {
|
||||
if (this.canvas2d === false || systemInfo.platform === "windows" || systemInfo.platform === "mac") {
|
||||
this.type2d = false;
|
||||
} else {
|
||||
this.type2d = true;
|
||||
@@ -656,12 +581,7 @@ export default {
|
||||
methods: {
|
||||
beforeInit() {
|
||||
this.mixinDatacomErrorMessage = null;
|
||||
if (
|
||||
typeof this.chartData === "object" &&
|
||||
this.chartData != null &&
|
||||
this.chartData.series !== undefined &&
|
||||
this.chartData.series.length > 0
|
||||
) {
|
||||
if (typeof this.chartData === "object" && this.chartData != null && this.chartData.series !== undefined && this.chartData.series.length > 0) {
|
||||
//拷贝一下chartData,为了opts变更后统一数据来源
|
||||
this.drawData = deepCloneAssign({}, this.chartData);
|
||||
this.mixinDatacomLoading = false;
|
||||
@@ -920,11 +840,7 @@ export default {
|
||||
ctx.canvas.willReadFrequently = true;
|
||||
cfu.option[cid].context = ctx;
|
||||
cfu.option[cid].rotateLock = cfu.option[cid].rotate;
|
||||
if (
|
||||
cfu.instance[cid] &&
|
||||
cfu.option[cid] &&
|
||||
cfu.option[cid].update === true
|
||||
) {
|
||||
if (cfu.instance[cid] && cfu.option[cid] && cfu.option[cid].update === true) {
|
||||
this._updataUChart(cid);
|
||||
} else {
|
||||
canvas.width = data.width * this.pixel;
|
||||
@@ -939,8 +855,7 @@ export default {
|
||||
}
|
||||
} else {
|
||||
this.showchart = false;
|
||||
this.mixinDatacomErrorMessage =
|
||||
"参数错误:开启2d模式后,未获取到dom节点,canvas-id:" + cid;
|
||||
this.mixinDatacomErrorMessage = "参数错误:开启2d模式后,未获取到dom节点,canvas-id:" + cid;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -1086,10 +1001,7 @@ export default {
|
||||
offset: offset,
|
||||
textList: tc.textList,
|
||||
formatter: (item, category, index, opts) => {
|
||||
if (
|
||||
typeof cfu.option[cid].tooltipFormat === "string" &&
|
||||
cfu.formatter[cfu.option[cid].tooltipFormat]
|
||||
) {
|
||||
if (typeof cfu.option[cid].tooltipFormat === "string" && cfu.formatter[cfu.option[cid].tooltipFormat]) {
|
||||
return cfu.formatter[cfu.option[cid].tooltipFormat](item, category, index, opts);
|
||||
} else {
|
||||
return this._tooltipDefault(item, category, index, opts);
|
||||
@@ -1099,10 +1011,7 @@ export default {
|
||||
} else {
|
||||
cfu.instance[cid].showToolTip(e, {
|
||||
formatter: (item, category, index, opts) => {
|
||||
if (
|
||||
typeof cfu.option[cid].tooltipFormat === "string" &&
|
||||
cfu.formatter[cfu.option[cid].tooltipFormat]
|
||||
) {
|
||||
if (typeof cfu.option[cid].tooltipFormat === "string" && cfu.formatter[cfu.option[cid].tooltipFormat]) {
|
||||
return cfu.formatter[cfu.option[cid].tooltipFormat](item, category, index, opts);
|
||||
} else {
|
||||
return this._tooltipDefault(item, category, index, opts);
|
||||
@@ -1224,19 +1133,10 @@ export default {
|
||||
if (cfu.option[cid].enableScroll === true && e.changedTouches.length == 1) {
|
||||
cfu.instance[cid].scroll(e);
|
||||
}
|
||||
if (
|
||||
this.ontap === true &&
|
||||
cfu.option[cid].enableScroll === false &&
|
||||
this.onmovetip === true
|
||||
) {
|
||||
if (this.ontap === true && cfu.option[cid].enableScroll === false && this.onmovetip === true) {
|
||||
this._tap(e, true);
|
||||
}
|
||||
if (
|
||||
this.ontouch === true &&
|
||||
cfu.option[cid].enableScroll === true &&
|
||||
this.onzoom === true &&
|
||||
e.changedTouches.length == 2
|
||||
) {
|
||||
if (this.ontouch === true && cfu.option[cid].enableScroll === true && this.onzoom === true && e.changedTouches.length == 2) {
|
||||
cfu.instance[cid].dobuleZoom(e);
|
||||
}
|
||||
this.emitMsg({
|
||||
@@ -1263,11 +1163,7 @@ export default {
|
||||
opts: cfu.instance[cid].opts,
|
||||
},
|
||||
});
|
||||
if (
|
||||
this.ontap === true &&
|
||||
cfu.option[cid].enableScroll === false &&
|
||||
this.onmovetip === true
|
||||
) {
|
||||
if (this.ontap === true && cfu.option[cid].enableScroll === false && this.onmovetip === true) {
|
||||
this._tap(e, true);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -19,33 +19,11 @@
|
||||
// 通用配置项
|
||||
|
||||
// 主题颜色配置:如每个图表类型需要不同主题,请在对应图表类型上更改color属性
|
||||
const color = [
|
||||
"#1890FF",
|
||||
"#91CB74",
|
||||
"#FAC858",
|
||||
"#EE6666",
|
||||
"#73C0DE",
|
||||
"#3CA272",
|
||||
"#FC8452",
|
||||
"#9A60B4",
|
||||
"#ea7ccc",
|
||||
];
|
||||
const color = ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"];
|
||||
|
||||
const cfe = {
|
||||
//demotype为自定义图表类型
|
||||
type: [
|
||||
"pie",
|
||||
"ring",
|
||||
"rose",
|
||||
"funnel",
|
||||
"line",
|
||||
"column",
|
||||
"area",
|
||||
"radar",
|
||||
"gauge",
|
||||
"candle",
|
||||
"demotype",
|
||||
],
|
||||
type: ["pie", "ring", "rose", "funnel", "line", "column", "area", "radar", "gauge", "candle", "demotype"],
|
||||
//增加自定义图表类型,如果需要categories,请在这里加入您的图表类型例如最后的"demotype"
|
||||
categories: ["line", "column", "area", "radar", "gauge", "candle", "demotype"],
|
||||
//instance为实例变量承载属性,option为eopts承载属性,不要删除
|
||||
|
||||
@@ -17,17 +17,7 @@
|
||||
*/
|
||||
|
||||
// 主题颜色配置:如每个图表类型需要不同主题,请在对应图表类型上更改color属性
|
||||
const color = [
|
||||
"#1890FF",
|
||||
"#91CB74",
|
||||
"#FAC858",
|
||||
"#EE6666",
|
||||
"#73C0DE",
|
||||
"#3CA272",
|
||||
"#FC8452",
|
||||
"#9A60B4",
|
||||
"#ea7ccc",
|
||||
];
|
||||
const color = ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"];
|
||||
|
||||
//事件转换函数,主要用作格式化x轴为时间轴,根据需求自行修改
|
||||
const formatDateTime = (timeStamp, returnType) => {
|
||||
@@ -61,66 +51,11 @@ const formatDateTime = (timeStamp, returnType) => {
|
||||
|
||||
const cfu = {
|
||||
//demotype为自定义图表类型,一般不需要自定义图表类型,只需要改根节点上对应的类型即可
|
||||
type: [
|
||||
"pie",
|
||||
"ring",
|
||||
"rose",
|
||||
"word",
|
||||
"funnel",
|
||||
"map",
|
||||
"arcbar",
|
||||
"line",
|
||||
"column",
|
||||
"mount",
|
||||
"bar",
|
||||
"area",
|
||||
"radar",
|
||||
"gauge",
|
||||
"candle",
|
||||
"mix",
|
||||
"tline",
|
||||
"tarea",
|
||||
"scatter",
|
||||
"bubble",
|
||||
"demotype",
|
||||
],
|
||||
range: [
|
||||
"饼状图",
|
||||
"圆环图",
|
||||
"玫瑰图",
|
||||
"词云图",
|
||||
"漏斗图",
|
||||
"地图",
|
||||
"圆弧进度条",
|
||||
"折线图",
|
||||
"柱状图",
|
||||
"山峰图",
|
||||
"条状图",
|
||||
"区域图",
|
||||
"雷达图",
|
||||
"仪表盘",
|
||||
"K线图",
|
||||
"混合图",
|
||||
"时间轴折线",
|
||||
"时间轴区域",
|
||||
"散点图",
|
||||
"气泡图",
|
||||
"自定义类型",
|
||||
],
|
||||
type: ["pie", "ring", "rose", "word", "funnel", "map", "arcbar", "line", "column", "mount", "bar", "area", "radar", "gauge", "candle", "mix", "tline", "tarea", "scatter", "bubble", "demotype"],
|
||||
range: ["饼状图", "圆环图", "玫瑰图", "词云图", "漏斗图", "地图", "圆弧进度条", "折线图", "柱状图", "山峰图", "条状图", "区域图", "雷达图", "仪表盘", "K线图", "混合图", "时间轴折线", "时间轴区域", "散点图", "气泡图", "自定义类型"],
|
||||
//增加自定义图表类型,如果需要categories,请在这里加入您的图表类型,例如最后的"demotype"
|
||||
//自定义类型时需要注意"tline","tarea","scatter","bubble"等时间轴(矢量x轴)类图表,没有categories,不需要加入categories
|
||||
categories: [
|
||||
"line",
|
||||
"column",
|
||||
"mount",
|
||||
"bar",
|
||||
"area",
|
||||
"radar",
|
||||
"gauge",
|
||||
"candle",
|
||||
"mix",
|
||||
"demotype",
|
||||
],
|
||||
categories: ["line", "column", "mount", "bar", "area", "radar", "gauge", "candle", "mix", "demotype"],
|
||||
//instance为实例变量承载属性,不要删除
|
||||
instance: {},
|
||||
//option为opts及eopts承载属性,不要删除
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user