"
+ exit 1
+fi
+
+
+DIR=./web/src/views/$1/$2
+
+
+# 设置数据库连接信息
+HOST="177.10.0.13"
+USER="root"
+PASSWORD=$(cat .env | grep MYSQL_PASSWORD | sed 's/^.*MYSQL_PASSWORD=//g')
+DATABASE="django-vue3-admin"
+TABLE=$3
+TARGET_FILE="./web/src/views/$1/$2/crud.tsx"
+
+
+# 表是否存在
+TABLE_EXISTS=$(mysql -h $HOST -u $USER -p$PASSWORD -D $DATABASE -e "SHOW TABLES LIKE '$TABLE';" -N | grep "$TABLE" | wc -l)
+
+if [ "$TABLE_EXISTS" -eq 0 ]; then
+ echo "Table $TABLE does not exist in database $DATABASE."
+ exit 1
+fi
+
+mkdir -p $DIR
+cp -r ./web/src/views/template/* $DIR
+sed -i "s/VIEWSETNAME/$2/g" $DIR/*
+
+sed -n -e :a -e '1,5!{P;N;D;};N;ba' -i $TARGET_FILE
+
+# 查询表结构
+QUERY="SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '$DATABASE' AND TABLE_NAME = '$TABLE' ORDER BY ORDINAL_POSITION;"
+
+# 使用 MySQL 查询获取字段信息,并生成 fast-crud 配置
+mysql -h $HOST -u $USER -p$PASSWORD -D $DATABASE -e "$QUERY" -N | while read COLUMN_NAME DATA_TYPE COLUMN_COMMENT IS_NULLABLE; do
+ # 映射 MySQL 数据类型到 fast-crud 类型
+ case "$DATA_TYPE" in
+ "int"|"bigint"|"smallint"|"mediumint"|"tinyint"|"decimal"|"float"|"double")
+ TYPE="number"
+ ;;
+ "date"|"datetime"|"timestamp")
+ TYPE="date"
+ ;;
+ *)
+ TYPE="text"
+ ;;
+ esac
+
+ echo " $COLUMN_NAME: {
+ title: '$COLUMN_NAME',
+ type: '$TYPE',
+ search: { show: true },
+ column: {
+ minWidth: 120,
+ sortable: 'custom',
+ },
+ form: {" >> $TARGET_FILE
+
+ if [ "$IS_NULLABLE" = "NO" ]; then
+ echo " helper: {
+ render() {
+ return $COLUMN_NAME 是必填的
;
+ }
+ },
+ rules: [{
+ required: true, message: '$COLUMN_NAME 是必填的'
+ }]," >> $TARGET_FILE
+ fi
+
+ echo " component: {
+ placeholder: '请输入 $COLUMN_NAME',
+ },
+ },
+ }," >> $TARGET_FILE
+done
+
+echo " },
+ },
+ };
+}" >> $TARGET_FILE
diff --git a/docker_env/web/Dockerfile b/docker_env/web/Dockerfile
index dc0cdb8b8ee85a1d04e18a7dfdb3a14916a2cffe..73fc4e7cd6d3d3cfd06d93260d2cdba4abfc5d20 100644
--- a/docker_env/web/Dockerfile
+++ b/docker_env/web/Dockerfile
@@ -1,4 +1,4 @@
-FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:16.19-alpine
+FROM registry.cn-zhangjiakou.aliyuncs.com/dvadmin-pro/dvadmin3-base-web:18.20-alpine
WORKDIR /web/
COPY web/. .
RUN yarn install --registry=https://registry.npmmirror.com
diff --git a/init.sh b/init.sh
index 8c377ed6f0d6c19d0a47cb941495d64f20b217d1..ab828cb56d912697f883f9c7699e0dc1490130b7 100644
--- a/init.sh
+++ b/init.sh
@@ -1,5 +1,6 @@
#!/bin/bash
ENV_FILE=".env"
+HOST="177.10.0.13"
# 检查 .env 文件是否存在
if [ -f "$ENV_FILE" ]; then
echo "$ENV_FILE 文件已存在。"
@@ -15,17 +16,60 @@ else
echo "REDIS随机密码已生成并写入 $ENV_FILE 文件。"
awk 'BEGIN { cmd="cp -i ./backend/conf/env.example.py ./backend/conf/env.py "; print "n" |cmd; }'
- sed -i "s|DATABASE_HOST = '127.0.0.1'|DATABASE_HOST = '177.10.0.13'|g" ./backend/conf/env.py
+ sed -i "s|DATABASE_HOST = '127.0.0.1'|DATABASE_HOST = '$HOST'|g" ./backend/conf/env.py
sed -i "s|REDIS_HOST = '127.0.0.1'|REDIS_HOST = '177.10.0.15'|g" ./backend/conf/env.py
sed -i "s|DATABASE_PASSWORD = 'DVADMIN3'|DATABASE_PASSWORD = '$MYSQL_PASSWORD'|g" ./backend/conf/env.py
sed -i "s|REDIS_PASSWORD = 'DVADMIN3'|REDIS_PASSWORD = '$REDIS_PASSWORD'|g" ./backend/conf/env.py
echo "初始化密码创建成功"
fi
+echo "正在启动容器..."
docker-compose up -d
-docker exec dvadmin3-django python manage.py makemigrations
-docker exec dvadmin3-django python manage.py migrate
-docker exec dvadmin3-django python manage.py init
-echo "欢迎使用dvadmin3项目"
-echo "登录地址:http://ip:8080"
-echo "如访问不到,请检查防火墙配置"
+
+if [ $? -ne 0 ]; then
+ echo "docker-compose up -d 执行失败!"
+ exit 1
+fi
+
+MYSQL_PORT=3306
+REDIS_PORT=6379
+
+check_mysql() {
+ if nc -z "$HOST" "$MYSQL_PORT" >/dev/null 2>&1; then
+ echo "MySQL 服务正在运行在 $HOST:$MYSQL_PORT"
+ return 0
+ else
+ return 1
+ fi
+}
+
+check_redis() {
+ if nc -z "$HOST" "$REDIS_PORT" >/dev/null 2>&1; then
+ echo "Redis 服务正在运行在 $HOST:$REDIS_PORT"
+ return 0
+ else
+ return 1
+ fi
+}
+
+i=1
+while [ $i -le 8 ]; do
+ if check_mysql || check_redis; then
+ echo "正在迁移数据..."
+ docker exec dvadmin3-django python3 manage.py makemigrations
+ docker exec dvadmin3-django python3 manage.py migrate
+ echo "正在初始化数据..."
+ docker exec dvadmin3-django python3 manage.py init
+ echo "欢迎使用dvadmin3项目"
+ echo "登录地址:http://ip:8080"
+ echo "如访问不到,请检查防火墙配置"
+ exit 0
+ else
+ echo "第 $i 次尝试:MySQL 或 REDIS服务未运行,等待 2 秒后重试..."
+ sleep 2
+ fi
+ i=$((i+1))
+done
+
+echo "尝试 5 次后,MySQL 或 REDIS服务仍未运行"
+exit 1
diff --git a/web/.env b/web/.env
index 0d828a796e2ea128786d272e30bac3ddf9f004a6..a77f48577b0acf8fbdb15e7a5b49e3d87fb8f426 100644
--- a/web/.env
+++ b/web/.env
@@ -1,6 +1,6 @@
# port 端口号
VITE_PORT = 8080
-VITE_API_URL = 'http://dvadmin3api.django.icu:8001'
+VITE_API_URL = 'http://127.0.0.1:8000'
# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN = false
diff --git a/web/.env.development b/web/.env.development
index 1c3ca5db362e1f7935aded834e08f0a46ed640a6..dc36b291b2cdf3991de42c0cb05648f397f5e173 100644
--- a/web/.env.development
+++ b/web/.env.development
@@ -2,7 +2,7 @@
ENV = 'development'
# 本地环境接口地址
-VITE_API_URL = 'http://127.0.0.1:8000'
+VITE_API_URL = 'http://127.0.0.1:8001'
# 是否启用按钮权限
VITE_PM_ENABLED = true
diff --git a/web/README.md b/web/README.md
index e70cc71da1995900a70d45a53017b86fde915a0c..86d36b1ee45b46b4db71a740ee495d329e756422 100644
--- a/web/README.md
+++ b/web/README.md
@@ -49,6 +49,10 @@
👩👦👦文档地址:[coding](https://dvadmin-private.coding.net/share/km/cec69f3d-30fe-47d5-bd97-e9e851f0b776/K-2)
+## 给框架点赞
+
+
+
## 交流
diff --git a/web/package.json b/web/package.json
index ca4c3067e397d9b46437faab6fecb285f043d2a6..61d8bc49da6b53bce7b88b574d4b192e8c89414f 100644
--- a/web/package.json
+++ b/web/package.json
@@ -5,6 +5,7 @@
"license": "MIT",
"scripts": {
"dev": "vite --force",
+ "build:dev":"vite build --mode development",
"build": "vite build",
"build:local": "vite build --mode local_prod",
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
@@ -15,6 +16,7 @@
"@fast-crud/fast-extends": "^1.21.2",
"@fast-crud/ui-element": "^1.21.2",
"@fast-crud/ui-interface": "^1.21.2",
+ "@great-dream/dvadmin3-celery-web": "^3.1.3",
"@iconify/vue": "^4.1.2",
"@types/lodash": "^4.17.7",
"@vitejs/plugin-vue-jsx": "^4.0.1",
diff --git a/web/src/App.vue b/web/src/App.vue
index 56f585e22b7aa9b7a27581bea53edc4f4454d015..c13df045b37daea76ba83127e15361baf2ff0fa5 100644
--- a/web/src/App.vue
+++ b/web/src/App.vue
@@ -11,7 +11,7 @@
@@ -172,7 +174,6 @@ defineExpose({
.user-info-head {
position: relative;
display: inline-block;
- height: 120px;
}
.user-info-head:hover:after {
diff --git a/web/src/components/fileSelector/index.vue b/web/src/components/fileSelector/index.vue
index 1e1862c348f8084092665c86a73af860b8e87d90..c8319aa423142a47c1965eafccbb5091e3c01666 100644
--- a/web/src/components/fileSelector/index.vue
+++ b/web/src/components/fileSelector/index.vue
@@ -8,7 +8,29 @@
-
+
@@ -24,10 +46,11 @@
-
+
+
+
{
while (!target.dataset.id) target = target.parentElement as HTMLElement;
let fileId = target.dataset.id;
if (props.multiple) {
+ if (!!!data.value) data.value = [];
if (target.classList.contains('active')) { target.classList.remove('active'); flat = -1; }
else { target.classList.add('active'); flat = 1; }
if (data.value.length) {
@@ -327,8 +352,12 @@ const clearState = () => {
// all数据不能清,因为all只会在挂载的时候赋值一次
// listAllData.value = [];
};
-const clear = () => { data.value = null; onDataChange(null); }
-
+const clear = () => { data.value = null; onDataChange(null); };
+const clearOne = (item: any) => {
+ let _l = (JSON.parse(JSON.stringify(data.value)) as any[]).filter((i: any) => i !== item)
+ data.value = _l;
+ onDataChange(_l);
+};
// 网络文件部分
const netLoading = ref(false);
@@ -386,7 +415,15 @@ watch(
const { ui } = useUi();
const formValidator = ui.formItem.injectFormItemContext();
const onDataChange = (value: any) => {
- emit('update:modelValue', value);
+ let _v = null;
+ if (value) {
+ if (typeof value === 'string') _v = value.replace(/\\/g, '/');
+ else {
+ _v = [];
+ for (let i of value) _v.push(i.replace(/\\/g, '/'));
+ }
+ }
+ emit('update:modelValue', _v);
formValidator.onChange();
formValidator.onBlur();
};
@@ -394,7 +431,8 @@ const onDataChange = (value: any) => {
defineExpose({ data, onDataChange, selectVisiable, clearState, clear });
onMounted(() => {
- if (props.multiple && props.inputType !== 'selector')
+
+ if (props.multiple && !['selector', 'image'].includes(props.inputType))
throw new Error('FileSelector组件属性multiple为true时inputType必须为selector');
listRequestAll();
console.log('fileselector tenentmdoe', isTenentMode);
@@ -475,4 +513,9 @@ onMounted(() => {
top: 2px;
cursor: pointer;
}
+
+.itemList {
+ border: 1px solid #dcdfe6;
+ border-radius: 8px;
+}
\ No newline at end of file
diff --git a/web/src/components/tableSelector/index.vue b/web/src/components/tableSelector/index.vue
index 8e8c91a9dc56db58087acc8c4ae2af414919c625..d827a751c70554113a27942963c25980d6a9d61e 100644
--- a/web/src/components/tableSelector/index.vue
+++ b/web/src/components/tableSelector/index.vue
@@ -3,6 +3,7 @@
popper-class="popperClass"
class="tableSelector"
multiple
+ :collapseTags="props.tableConfig.collapseTags"
@remove-tag="removeTag"
v-model="data"
placeholder="请选择"
@@ -18,20 +19,22 @@
-
+
diff --git a/web/src/layout/navBars/breadcrumb/user.vue b/web/src/layout/navBars/breadcrumb/user.vue
index 61793c974ad275979387c36293f24ad9dcc3d931..351bf259095fb9d00ac43ccd659dc0c73248d878 100644
--- a/web/src/layout/navBars/breadcrumb/user.vue
+++ b/web/src/layout/navBars/breadcrumb/user.vue
@@ -37,7 +37,7 @@
-
+
@@ -58,7 +58,7 @@
>
-
+
-
+
@@ -93,7 +93,7 @@
{{ $t('message.user.dropdown1') }}
{{ $t('message.user.dropdown2') }}
- {{ $t('message.user.dropdown6') }}
+ 更新日志
{{ $t('message.user.dropdown5') }}
@@ -250,6 +250,7 @@ onMounted(() => {
//消息中心的未读数量
import { messageCenterStore } from '/@/stores/messageCenter';
+import {getBaseURL} from "/@/utils/baseUrl";
const messageCenter = messageCenterStore();
diff --git a/web/src/layout/navMenu/horizontal.vue b/web/src/layout/navMenu/horizontal.vue
index 650fb1485f7a5b98b535574799050c5ff2c06c28..e7fcfca0737c7d6f9347b20cff9839d9e43a7810 100644
--- a/web/src/layout/navMenu/horizontal.vue
+++ b/web/src/layout/navMenu/horizontal.vue
@@ -1,8 +1,8 @@