!5 news:增加最新版本的sql

Merge pull request !5 from wenhao543/dev
This commit is contained in:
wenhao543
2025-09-23 05:20:41 +00:00
committed by Gitee
612 changed files with 108031 additions and 68 deletions
Binary file not shown.
@@ -199,8 +199,8 @@ public class NetworkServiceImpl implements INetworkService {
String type = bo.getType();
String productKey = bo.getProductKey();
String unionId = bo.getUnionId();
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId()
.equals(type)) {
if (NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.TCP_SERVER.getId().equals(type)) {
if (tcpClusterService != null && tcpServerManager != null) {
Object serverInstance = tcpServerManager.getServerInstance(productKey);
if (serverInstance != null && tcpServerManager.isAlive(serverInstance)) {
@@ -216,14 +216,14 @@ public class NetworkServiceImpl implements INetworkService {
}
// 查出哪些TCP-Server绑定了产品
if (NetworkType.TCP_SERVER.getId().equals(type)) {
IoTProductBO ioTProductBO = ioTProductMapper.selectTcpProductsUseNetwork(
bo.getProductKey());
IoTProductBO ioTProductBO =
ioTProductMapper.selectTcpProductsUseNetwork(bo.getProductKey());
bo.setBindTcpServerProductCount(ioTProductBO == null ? 0 : 1);
bo.setBindTcpServerProducts(ioTProductBO);
}
} else if (NetworkType.MQTT_CLIENT.getId().equals(type) || NetworkType.MQTT_SERVER.getId()
.equals(type)) {
} else if (NetworkType.MQTT_CLIENT.getId().equals(type)
|| NetworkType.MQTT_SERVER.getId().equals(type)) {
List<IoTProductBO> ioTProductBOS = ioTProductMapper.selectMqttProductsUseNetwork(unionId);
bo.setBindMqttServerProductCount(CollUtil.size(ioTProductBOS));
bo.setBindMqttServerProducts(ioTProductBOS);
@@ -294,8 +294,8 @@ public class NetworkServiceImpl implements INetworkService {
String type = network.getType();
String productKey = network.getProductKey();
String unionId = network.getUnionId();
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId()
.equals(type)) {
if (NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.TCP_SERVER.getId().equals(type)) {
if (tcpClusterService != null && tcpServerManager != null) {
Object serverInstance = tcpServerManager.getServerInstance(productKey);
if (serverInstance != null && tcpServerManager.isAlive(serverInstance)) {
@@ -310,8 +310,8 @@ public class NetworkServiceImpl implements INetworkService {
vo.setRunning(Boolean.FALSE);
}
} else if (NetworkType.MQTT_CLIENT.getId().equals(type) || NetworkType.MQTT_SERVER.getId()
.equals(type)) {
} else if (NetworkType.MQTT_CLIENT.getId().equals(type)
|| NetworkType.MQTT_SERVER.getId().equals(type)) {
if (mqttServerManager != null && mqttServerManager.isConnected(unionId)) {
vo.setStateName("已启动");
vo.setRunning(Boolean.TRUE);
@@ -348,8 +348,9 @@ public class NetworkServiceImpl implements INetworkService {
}
// 常用端口
Set<Integer> commonPorts = new HashSet<>();
int[] arr = {80, 443, 3306, 6379, 8080, 22, 21, 25, 53, 123, 1521, 5432, 8888, 9000, 5000, 7001,
27017};
int[] arr = {
80, 443, 3306, 6379, 8080, 22, 21, 25, 53, 123, 1521, 5432, 8888, 9000, 5000, 7001, 27017
};
for (int p : arr) {
commonPorts.add(p);
}
@@ -389,8 +390,9 @@ public class NetworkServiceImpl implements INetworkService {
network.setState(false);
// 新增TCP_SERVER/UDP时,必须指定productKey,并且不能重复绑定
if (NetworkType.TCP_SERVER.getId().equals(network.getType()) || NetworkType.TCP_CLIENT.getId()
.equals(network.getType()) || NetworkType.UDP.getId().equals(network.getType())) {
if (NetworkType.TCP_SERVER.getId().equals(network.getType())
|| NetworkType.TCP_CLIENT.getId().equals(network.getType())
|| NetworkType.UDP.getId().equals(network.getType())) {
if (StrUtil.isBlank(network.getProductKey())) {
throw new RuntimeException("TCP/UDP服务组件必须选择产品");
}
@@ -405,8 +407,8 @@ public class NetworkServiceImpl implements INetworkService {
int result = networkMapper.insertNetwork(network);
// 新增成功后,若为TCP_SERVER,更新iot_product表
if (result > 0 && NetworkType.TCP_SERVER.getId().equals(network.getType())) {
ioTProductMapper.updateNetworkUnionIdByProductKey(network.getProductKey(),
network.getProductKey());
ioTProductMapper.updateNetworkUnionIdByProductKey(
network.getProductKey(), network.getProductKey());
}
return result;
}
@@ -432,7 +434,8 @@ public class NetworkServiceImpl implements INetworkService {
}
String type = existNetwork.getType();
if ((NetworkType.TCP_SERVER.getId().equals(type) || NetworkType.TCP_CLIENT.getId().equals(type)
if ((NetworkType.TCP_SERVER.getId().equals(type)
|| NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.UDP.getId().equals(type))) {
// 只有当unionId发生变化时,才做"已被产品关联且产品下有设备时禁止修改"的校验
String oldProductKey = existNetwork.getProductKey();
@@ -450,8 +453,8 @@ public class NetworkServiceImpl implements INetworkService {
checkPortAvailable(network, true);
int count = networkMapper.updateNetwork(network);
if (count > 0 && NetworkType.TCP_SERVER.getId().equals(network.getType())) {
ioTProductMapper.updateNetworkUnionIdByProductKey(network.getProductKey(),
network.getProductKey());
ioTProductMapper.updateNetworkUnionIdByProductKey(
network.getProductKey(), network.getProductKey());
}
return count;
}
@@ -465,7 +468,8 @@ public class NetworkServiceImpl implements INetworkService {
}
String type = network.getType();
// 只对TCP_SERVER、TCP_CLIENT和UDP做判断
if (NetworkType.TCP_SERVER.getId().equals(type) || NetworkType.TCP_CLIENT.getId().equals(type)
if (NetworkType.TCP_SERVER.getId().equals(type)
|| NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.UDP.getId().equals(type)) {
// 查找是否有关联产品
String productKey = ioTProductMapper.findProductKeyByNetworkUnionId(network.getUnionId());
@@ -495,14 +499,14 @@ public class NetworkServiceImpl implements INetworkService {
}
String type = network.getType();
JSONObject config = JSONUtil.parseObj(network.getConfiguration());
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId()
.equals(type)) {
if (NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.TCP_SERVER.getId().equals(type)) {
Integer port = config.getInt("port");
if (port == null) {
throw new RuntimeException("TCP网络组件启动/重启时端口不能为空");
}
} else if (NetworkType.MQTT_CLIENT.getId().equals(type) || NetworkType.MQTT_SERVER.getId()
.equals(type)) {
} else if (NetworkType.MQTT_CLIENT.getId().equals(type)
|| NetworkType.MQTT_SERVER.getId().equals(type)) {
String host = config.getStr("host");
if (StrUtil.isBlank(host)) {
throw new RuntimeException("MQTT网络组件启动/重启时host不能为空");
@@ -521,7 +525,7 @@ public class NetworkServiceImpl implements INetworkService {
log.info("启动网络组件成功: {}", network.getName());
return 1;
} else {
throw new RuntimeException("启动失败: " + network.getName());
throw new IoTException("系统协议类型模块不存在,请检查");
}
} catch (Exception e) {
log.error("启动网络组件失败: {}", network.getName(), e);
@@ -561,14 +565,14 @@ public class NetworkServiceImpl implements INetworkService {
}
String type = network.getType();
JSONObject config = JSONUtil.parseObj(network.getConfiguration());
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId()
.equals(type)) {
if (NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.TCP_SERVER.getId().equals(type)) {
Integer port = config.getInt("port");
if (port == null) {
throw new RuntimeException("TCP网络组件启动/重启时端口不能为空");
}
} else if (NetworkType.MQTT_CLIENT.getId().equals(type) || NetworkType.MQTT_SERVER.getId()
.equals(type)) {
} else if (NetworkType.MQTT_CLIENT.getId().equals(type)
|| NetworkType.MQTT_SERVER.getId().equals(type)) {
String host = config.getStr("host");
if (StrUtil.isBlank(host)) {
throw new RuntimeException("MQTT网络组件启动/重启时host不能为空");
@@ -598,8 +602,12 @@ public class NetworkServiceImpl implements INetworkService {
@Override
public List<String> getNetworkTypes() {
return Arrays.asList(NetworkType.TCP_CLIENT.getId(), NetworkType.TCP_SERVER.getId(),
NetworkType.MQTT_CLIENT.getId(), NetworkType.MQTT_SERVER.getId(), NetworkType.UDP.getId());
return Arrays.asList(
NetworkType.TCP_CLIENT.getId(),
NetworkType.TCP_SERVER.getId(),
NetworkType.MQTT_CLIENT.getId(),
NetworkType.MQTT_SERVER.getId(),
NetworkType.UDP.getId());
}
/**
@@ -660,7 +668,8 @@ public class NetworkServiceImpl implements INetworkService {
JSONObject config = JSONUtil.parseObj(network.getConfiguration());
String type = network.getType();
// TCP/UDP类型检查端口唯一性
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId().equals(type)
if (NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.TCP_SERVER.getId().equals(type)
|| NetworkType.UDP.getId().equals(type)) {
Integer port = config.getInt("port");
if (port != null) {
@@ -674,18 +683,24 @@ public class NetworkServiceImpl implements INetworkService {
}
// MQTT类型检查主机和用户名组合唯一性
if (NetworkType.MQTT_CLIENT.getId().equals(type) || NetworkType.MQTT_SERVER.getId()
.equals(type)) {
if (NetworkType.MQTT_CLIENT.getId().equals(type)
|| NetworkType.MQTT_SERVER.getId().equals(type)) {
String host = config.getStr("host");
String username = config.getStr("username");
if (StrUtil.isNotBlank(host) && StrUtil.isNotBlank(username)) {
List<Network> existingNetworks = networkMapper.selectMqttNetworkByHostAndUsername(host,
username, excludeId);
List<Network> existingNetworks =
networkMapper.selectMqttNetworkByHostAndUsername(host, username, excludeId);
if (!existingNetworks.isEmpty()) {
Network existingNetwork = existingNetworks.get(0);
throw new RuntimeException("主机 " + host + " 和用户名 " + username + " 的组合已被 "
+ existingNetwork.getName() + " 网络组件使用");
throw new RuntimeException(
"主机 "
+ host
+ " 和用户名 "
+ username
+ " 的组合已被 "
+ existingNetwork.getName()
+ " 网络组件使用");
}
}
}
@@ -705,24 +720,18 @@ public class NetworkServiceImpl implements INetworkService {
*/
private boolean startNetworkByType(Network network) {
String type = network.getType();
try {
switch (type) {
case NetworkTypeConstants.TCP_CLIENT:
case NetworkTypeConstants.TCP_SERVER:
return startTcpNetwork(network);
case NetworkTypeConstants.MQTT_CLIENT:
case NetworkTypeConstants.MQTT_SERVER:
return startMqttNetwork(network);
case NetworkTypeConstants.UDP:
return startUdpNetwork(network);
default:
log.warn("不支持的网络类型: {}", type);
return false;
}
} catch (Exception e) {
log.error("启动网络组件失败: type={}, name={}", type, network.getName(), e);
return false;
switch (type) {
case NetworkTypeConstants.TCP_CLIENT:
case NetworkTypeConstants.TCP_SERVER:
return startTcpNetwork(network);
case NetworkTypeConstants.MQTT_CLIENT:
case NetworkTypeConstants.MQTT_SERVER:
return startMqttNetwork(network);
case NetworkTypeConstants.UDP:
return startUdpNetwork(network);
default:
log.warn("不支持的网络类型: {}", type);
return false;
}
}
@@ -792,7 +801,7 @@ public class NetworkServiceImpl implements INetworkService {
private boolean startTcpNetwork(Network network) {
if (tcpClusterService == null) {
log.error("TCP服务管理器未注入,无法启动TCP网络组件: {}", network.getName());
return false;
throw new IoTException("TCP模块未注入,无法启动TCP网络组件");
}
String productKey = network.getProductKey();
@@ -824,8 +833,8 @@ public class NetworkServiceImpl implements INetworkService {
*/
private boolean stopTcpNetwork(Network network) {
if (tcpClusterService == null) {
log.error("TCP服务管理器未注入,无法停止TCP网络组件: {}", network.getName());
return false;
log.error("TCP服务管理器未注入,无法启动TCP网络组件: {}", network.getName());
throw new IoTException("TCP模块未注入,无法启动TCP网络组件");
}
String productKey = network.getProductKey();
@@ -856,8 +865,8 @@ public class NetworkServiceImpl implements INetworkService {
*/
private boolean restartTcpNetwork(Network network) {
if (tcpClusterService == null) {
log.error("TCP服务管理器未注入,无法重启TCP网络组件: {}", network.getName());
return false;
log.error("TCP服务管理器未注入,无法启动TCP网络组件: {}", network.getName());
throw new IoTException("TCP模块未注入,无法启动TCP网络组件");
}
String productKey = network.getProductKey();
@@ -888,8 +897,7 @@ public class NetworkServiceImpl implements INetworkService {
*/
private boolean startMqttNetwork(Network network) {
if (mqttServerManager == null) {
log.error("MQTT服务管理器未注入,无法启动MQTT网络组件: {}", network.getName());
return false;
throw new IoTException("MQTT模块未注入,无法启动TCP网络组件");
}
String unionId = network.getUnionId();
@@ -920,10 +928,8 @@ public class NetworkServiceImpl implements INetworkService {
*/
private boolean stopMqttNetwork(Network network) {
if (mqttServerManager == null) {
log.error("MQTT服务管理器未注入,无法停止MQTT网络组件: {}", network.getName());
return false;
throw new IoTException("MQTT模块未注入,无法启动TCP网络组件");
}
String unionId = network.getUnionId();
if (StrUtil.isBlank(unionId)) {
log.error("MQTT网络组件缺少unionId: {}", network.getName());
@@ -952,8 +958,7 @@ public class NetworkServiceImpl implements INetworkService {
*/
private boolean restartMqttNetwork(Network network) {
if (mqttServerManager == null) {
log.error("MQTT服务管理器未注入,无法重启MQTT网络组件: {}", network.getName());
return false;
throw new IoTException("MQTT模块未注入,无法启动TCP网络组件");
}
String unionId = network.getUnionId();
@@ -988,6 +993,10 @@ public class NetworkServiceImpl implements INetworkService {
* @return 是否成功
*/
private boolean startUdpNetwork(Network network) {
if (udpClusterService == null) {
log.error("UDP服务管理器未注入,无法启动TCP网络组件: {}", network.getName());
throw new IoTException("UDP模块未注入,无法启动UDP网络组件");
}
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("UDP网络组件缺少productKey: {}", network.getName());
@@ -1018,6 +1027,10 @@ public class NetworkServiceImpl implements INetworkService {
* @return 是否成功
*/
private boolean stopUdpNetwork(Network network) {
if (udpClusterService == null) {
log.error("UDP服务管理器未注入,无法启动TCP网络组件: {}", network.getName());
throw new IoTException("UDP模块未注入,无法启动UDP网络组件");
}
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("UDP网络组件缺少productKey: {}", network.getName());
@@ -1048,6 +1061,10 @@ public class NetworkServiceImpl implements INetworkService {
* @return 是否成功
*/
private boolean restartUdpNetwork(Network network) {
if (udpClusterService == null) {
log.error("UDP服务管理器未注入,无法启动TCP网络组件: {}", network.getName());
throw new IoTException("UDP模块未注入,无法启动UDP网络组件");
}
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("UDP网络组件缺少productKey: {}", network.getName());
+3
View File
@@ -0,0 +1,3 @@
> 1%
last 2 versions
not ie <= 10
+34
View File
@@ -0,0 +1,34 @@
# 排除源代码和开发文件
src/
node_modules/
package*.json
webpack.config.js
vite.config.js
tsconfig.json
.eslintrc.js
.prettierrc
# 排除 IDE 文件
.idea/
.vscode/
*.iml
# 排除版本控制文件
.git/
.gitignore
# 排除构建产物 (只保留 dist 目录)
!dist/
# 排除日志文件
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 排除环境文件
.env
.env.local
.env.development
.env.test
.env.production
+39
View File
@@ -0,0 +1,39 @@
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = false
indent_style = space
indent_size = 2
[{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}]
indent_style = space
indent_size = 2
[{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}]
indent_style = space
indent_size = 2
[{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}]
indent_style = space
indent_size = 2
[*.svg]
indent_style = space
indent_size = 2
[*.js.map]
indent_style = space
indent_size = 2
[*.less]
indent_style = space
indent_size = 2
[*.vue]
indent_style = space
indent_size = 2
[{.analysis_options,*.yml,*.yaml}]
indent_style = space
indent_size = 2
+3
View File
@@ -0,0 +1,3 @@
NODE_ENV=production
VUE_APP_PREVIEW=false
VUE_APP_BASE_API=/api
+7
View File
@@ -0,0 +1,7 @@
NODE_ENV=development
VUE_APP_PREVIEW=true
VUE_APP_BASE_API=/api
# VUE_APP_API_PATH = http://192.168.31.194:9092
# VUE_APP_API_PATH = http://127.0.0.1:9091
VUE_APP_API_PATH = http://127.0.0.1:9092
+3
View File
@@ -0,0 +1,3 @@
NODE_ENV=production
VUE_APP_PREVIEW=false
VUE_APP_BASE_API=/api
+7
View File
@@ -0,0 +1,7 @@
docs/
node_modules
dist/
tests/
build/
vue.config.js
public/
+67
View File
@@ -0,0 +1,67 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: ['plugin:vue/strongly-recommended', '@vue/standard'],
globals: {
AMap: true,
AMapUI: true,
},
rules: {
'no-console': 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'generator-star-spacing': 'off',
'no-mixed-operators': 0,
'no-trailing-spaces': 'off',
'no-multiple-empty-lines': 'off',
'vue/max-attributes-per-line': 'off',
'vue/attribute-hyphenation': 0,
'vue/html-self-closing': 0,
'vue/component-name-in-template-casing': 0,
'vue/html-closing-bracket-spacing': 0,
'vue/singleline-html-element-content-newline': 0,
'vue/no-unused-components': 0,
'vue/multiline-html-element-content-newline': 0,
'vue/no-use-v-if-with-v-for': 0,
'vue/html-closing-bracket-newline': 0,
'vue/no-parsing-error': 0,
'no-tabs': 0,
quotes: [
2,
'single',
{
avoidEscape: true,
allowTemplateLiterals: true,
},
],
semi: [
2,
'never',
{
beforeStatementContinuationChars: 'never',
},
],
'no-delete-var': 2,
'prefer-const': [
2,
{
ignoreReadBeforeAssign: false,
},
],
'template-curly-spacing': 'off',
indent: 'off',
},
parserOptions: {
parser: '@babel/eslint-parser',
},
overrides: [
{
files: ['**/__tests__/*.{j,t}s?(x)',
'**/tests/unit/**/*.spec.{j,t}s?(x)'],
env: {
jest: true,
},
},
],
}
+1
View File
@@ -0,0 +1 @@
public/* linguist-vendored
+25
View File
@@ -0,0 +1,25 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
.eslintcache
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw*
package-lock.json
yarn.lock
+1
View File
@@ -0,0 +1 @@
_
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged
+6
View File
@@ -0,0 +1,6 @@
{
"printWidth": 120,
"semi": false,
"singleQuote": true,
"prettier.spaceBeforeFunctionParen": true
}
@@ -0,0 +1,86 @@
# SpecStory Artifacts Directory
This directory is automatically created and maintained by the SpecStory extension to preserve your
AI chat history.
## What's Here?
- `.specstory/history`: Contains auto-saved markdown files of your AI coding sessions
- Each file represents a separate AI chat session
- If you enable auto-save, files are automatically updated as you work
- You can enable/disable the auto-save feature in the SpecStory settings, it is disabled by
default
- `.specstory/.project.json`: Contains the persistent project identity for the current workspace
- This file is only present if you enable AI rules derivation
- This is used to provide consistent project identity of your project, even as the workspace is
moved or renamed
- `.specstory/ai_rules_backups`: Contains backups of the `.cursor/rules/derived-cursor-rules.mdc` or
the `.github/copilot-instructions.md` file
- Backups are automatically created each time the `.cursor/rules/derived-cursor-rules.mdc` or the
`.github/copilot-instructions.md` file is updated
- You can enable/disable the AI Rules derivation feature in the SpecStory settings, it is disabled
by default
- `.specstory/.gitignore`: Contains directives to exclude non-essential contents of the `.specstory`
directory from version control
- Add `/history` to exclude the auto-saved chat history from version control
## Valuable Uses
- Capture: Keep your context window up-to-date when starting new Chat/Composer sessions via @
references
- Search: For previous prompts and code snippets
- Learn: Meta-analyze your patterns and learn from your past experiences
- Derive: Keep the AI on course with your past decisions by automatically deriving rules from your
AI interactions
## Version Control
We recommend keeping this directory under version control to maintain a history of your AI
interactions. However, if you prefer not to version these files, you can exclude them by adding this
to your `.gitignore`:
```
.specstory/**
```
We recommend __not__ keeping the `.specstory/ai_rules_backups` directory under version control if
you are already using git to version your AI rules, and committing regularly. You can exclude it by
adding this to your `.gitignore`:
```
.specstory/ai_rules_backups
```
## Searching Your Codebase
When searching your codebase, search results may include your previous AI coding interactions. To
focus solely on your actual code files, you can exclude the AI interaction history from search
results.
To exclude AI interaction history:
1. Open the "Find in Files" search in Cursor or VSCode (Cmd/Ctrl + Shift + F)
2. Navigate to the "files to exclude" section
3. Add the following pattern:
```
.specstory/*
```
This will ensure your searches only return results from your working codebase files.
## Notes
- Auto-save only works when Cursor or VSCode flushes sqlite database data to disk. This results in a
small delay after the AI response is complete before SpecStory can save the history.
## Settings
You can control auto-saving behavior in Cursor or VSCode:
1. Open Cursor/Code → Settings → VS Code Settings (Cmd/Ctrl + ,)
2. Search for "SpecStory"
3. Find "Auto Save" setting to enable/disable
Auto-save occurs when changes are detected in the sqlite database, or every 2 minutes as a safety
net.
+7
View File
@@ -0,0 +1,7 @@
language: node_js
node_js:
- 10.15.0
cache: yarn
script:
- yarn
- yarn run lint --no-fix && yarn run build
+18
View File
@@ -0,0 +1,18 @@
# 使用 nginx 作为基础镜像
FROM nginx:alpine
# 复制前端构建文件
COPY dist/ /usr/share/nginx/html/
# 复制 nginx 配置
COPY nginx.conf /etc/nginx/nginx.conf
# 暴露端口
EXPOSE 80
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:80/ || exit 1
# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]
+31
View File
@@ -0,0 +1,31 @@
# 使用 CentOS 作为基础镜像
FROM centos:8
# 安装 nginx 和必要工具
RUN yum update -y && yum install -y \
nginx \
curl \
wget \
&& yum clean all
# 复制前端构建文件
COPY dist/ /usr/share/nginx/html/
# 复制 nginx 配置
COPY nginx.conf /etc/nginx/nginx.conf
# 创建必要的目录
RUN mkdir -p /var/log/nginx /var/cache/nginx
# 设置权限
RUN chown -R nginx:nginx /usr/share/nginx/html /var/log/nginx /var/cache/nginx
# 暴露端口
EXPOSE 80
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:80/health || exit 1
# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]
+31
View File
@@ -0,0 +1,31 @@
# 使用已经下载的 openjdk:21 镜像作为基础,安装 nginx
FROM openjdk:21
# 安装 nginx 和必要工具
RUN microdnf update -y && microdnf install -y \
nginx \
curl \
wget \
&& microdnf clean all
# 复制前端构建文件
COPY dist/ /usr/share/nginx/html/
# 复制 nginx 配置
COPY nginx.conf /etc/nginx/nginx.conf
# 创建必要的目录
RUN mkdir -p /var/log/nginx /var/cache/nginx
# 设置权限
RUN chown -R nginx:nginx /usr/share/nginx/html /var/log/nginx /var/cache/nginx
# 暴露端口
EXPOSE 80
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:80/health || exit 1
# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]
+18
View File
@@ -0,0 +1,18 @@
# 使用 Python 内置 HTTP 服务器作为最简单的静态文件服务
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 复制前端构建文件
COPY dist/ ./
# 暴露端口
EXPOSE 80
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:80/health || exit 1
# 启动 Python HTTP 服务器
CMD ["python", "-m", "http.server", "80"]
+31
View File
@@ -0,0 +1,31 @@
# 使用 Ubuntu 作为基础镜像
FROM ubuntu:22.04
# 安装 nginx 和必要工具
RUN apt-get update && apt-get install -y \
nginx \
curl \
wget \
&& rm -rf /var/lib/apt/lists/*
# 复制前端构建文件
COPY dist/ /var/www/html/
# 复制 nginx 配置
COPY nginx.conf /etc/nginx/nginx.conf
# 创建必要的目录
RUN mkdir -p /var/log/nginx /var/cache/nginx
# 设置权限
RUN chown -R www-data:www-data /var/www/html /var/log/nginx /var/cache/nginx
# 暴露端口
EXPOSE 80
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:80/health || exit 1
# 启动 nginx
CMD ["nginx", "-g", "daemon off;"]
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 扶醉
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+129
View File
@@ -0,0 +1,129 @@
<h1 align="center">RuoYi-Antdv</h1>
> RuoYi-Antdv 是RuoYi-Vue和Ant Design Vue Pro的结合。
<p align="center">
<a href="https://gitee.com/fuzui/RuoYi-Antdv" target="_blank"><img src="https://gitee.com/fuzui/RuoYi-Antdv/badge/star.svg?theme=dark" alt="Build Status"></a>&nbsp;
<a href="https://github.com/fuzui/RuoYi-Antdv" target="_blank"><img src="https://img.shields.io/github/stars/fuzui/RuoYi-Antdv.svg?style=social" alt="Build Status"></a>&nbsp;
<a href="https://gitee.com/y_project/RuoYi-Vue" target="_blank"><img src="https://img.shields.io/badge/RuoYi Vue-3.8.0-brightgreen" alt="Build Status"></a>&nbsp;
<a href="https://github.com/vueComponent/ant-design-vue" target="_blank"><img src="https://img.shields.io/badge/Ant Design Vue-1.7.8-brightgreen" alt="Build Status"></a>
</p>
------------------------------
## 简介
**RuoYi-Antdv**,使用[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue)作为后端,改其原有Element
Ui为Ant Design Vue。将会持续完全适配RuoYi-Vue。
> RuoYi-Vue是基于SpringBoot,Spring Security,JWT,Vue 的前后端分离权限管理系统。
>
> 拥有用户管理、部门管理、岗位管理、菜单管理、角色管理、字典管理、参数管理、通知公告、操作日志、登录日志、在线用户、定时任务、代码生成、系统接口、服务监控、在线构建器、连接池监视等功能。
*当前暂不支持RuoYi中的在线构建器功能。*
* 预览:[https://ruoyi.setworld.net/](https://ruoyi.setworld.net/)
* 文档:[https://docs.geekera.cn/RuoYi-Antdv/](https://docs.geekera.cn/RuoYi-Antdv/)
* RuoYi-Vue文档:[https://doc.ruoyi.vip/ruoyi-vue/](https://doc.ruoyi.vip/ruoyi-vue/)
* Ant Design
Vue文档:[https://www.antdv.com/docs/vue/introduce-cn/](https://www.antdv.com/docs/vue/introduce-cn/)
## 开始使用
1. 环境准备
* 运行启动RuoYi-Vue,参考[RuoYi文档](https://doc.ruoyi.vip/ruoyi-vue/)
* 安装[node](http://nodejs.org/)和[git](https://git-scm.com/)
2. 安装
```shell
git clone git@gitee.com:fuzui/RuoYi-Antdv.git
```
或
```shell
git clone git@github.com:fuzui/RuoYi-Antdv.git
```
3. 本地开发
进入项目根目录
```shell
npm install
```
> 若耗时太长可使用`npm install --registry=https://registry.npm.taobao.org`
```shell
npm run serve
```
> 打开浏览器访问 [http://localhost:8000](http://localhost:8080/)
4. 部署
[RuoYi-Antdv部署](https://docs.geekera.cn/RuoYi-Antdv/use.html)
## 注意事项
* 若需使用代码生成,请按照文档描述修改:
[代码生成前端适配](./docs/gen/)
## 致谢
* [RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue) 基于SpringBoot,Spring Security,JWT,Vue
的前后端分离权限管理系统
* [Ant Design Vue](https://github.com/vueComponent/ant-design-vue/) An enterprise-class UI
components based on Ant Design and Vue
* [Ant Design Vue Pro](https://github.com/vueComponent/ant-design-vue-pro) Use Ant Design Vue like a
Pro
* [wangEditor](https://github.com/wangeditor-team/wangEditor) 轻量级web富文本框
## 推荐
* [RuoYi-Cloud-Ant-Design](https://gitee.com/xuezipeng/ruoyi-cloud-ant-design) RuoYi-Cloud的Ant
Design(Antdv)版前端
* [RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud) 基于Spring Boot、Spring Cloud &
Alibaba的分布式微服务架构权限管理系统
## 联系
如果您发现了什么bug,或者有什么界面建议或意见,
欢迎 [issue](https://github.com/fuzui/RuoYi-Antdv/issues)
`RuoYi-Antdv(SpringBoot/SpringCloud)`群:
[![加入QQ群](https://img.shields.io/badge/Q群-1038609759-blue.svg)](https://jq.qq.com/?_wv=1027&k=Cq8fZnrj)
如果讨论后端,推荐添加若依前后端分离交流群(官方):
[![加入QQ群](https://img.shields.io/badge/Q群-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs)
## 演示图
<table>
<tr>
<td><img src="https://oss.fuzui.net/img/20210102022024.png"/></td>
<td><img src="https://oss.fuzui.net/img/20210102022136.png"/></td>
</tr>
<tr>
<td><img src="https://oss.fuzui.net/img/20210102022247.png"/></td>
<td><img src="https://oss.fuzui.net/img/20210102022534.png"/></td>
</tr>
<tr>
<td><img src="https://oss.fuzui.net/img/20210102022749.png"/></td>
<td><img src="https://oss.fuzui.net/img/20210102023153.png"/></td>
</tr>
</table>
+28
View File
@@ -0,0 +1,28 @@
const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV)
const plugins = []
if (IS_PROD) {
plugins.push('transform-remove-console')
}
// lazy load ant-design-vue
// if your use import on Demand, Use this code
plugins.push(['import', {
'libraryName': 'ant-design-vue',
'libraryDirectory': 'es',
'style': true // `style: true` 会加载 less 文件
}])
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset',
[
'@babel/preset-env',
{
'useBuiltIns': 'entry',
'corejs': 3
}
]
],
plugins
}
@@ -0,0 +1,52 @@
const ThemeColorReplacer = require('webpack-theme-color-replacer')
const generate = require('@ant-design/colors').generate
const getAntdSerials = (color) => {
// 淡化(即less的tint)
const lightens = new Array(9).fill().map((t, i) => {
return ThemeColorReplacer.varyColor.lighten(color, i / 10)
})
const colorPalettes = generate(color)
const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(
',')
return lightens.concat(colorPalettes).concat(rgb)
}
const themePluginOption = {
fileName: 'css/theme-colors-[contenthash:8].css',
matchColors: getAntdSerials('#1890ff'), // 主色系列
// 改变样式选择器,解决样式覆盖问题
changeSelector(selector) {
switch (selector) {
case '.ant-calendar-today .ant-calendar-date':
return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)'
+ selector
case '.ant-btn:focus,.ant-btn:hover':
return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-btn.active,.ant-btn:active':
return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)'
case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon':
case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon':
return ':not(.ant-steps-item-process)' + selector
// fixed https://github.com/vueComponent/ant-design-vue-pro/issues/876
case '.ant-steps-item-process .ant-steps-item-icon':
return ':not(.ant-steps-item-custom)' + selector
case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover':
case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover':
return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover'
case '.ant-menu-horizontal > .ant-menu-item-selected > a':
case '.ant-menu-horizontal>.ant-menu-item-selected>a':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a'
case '.ant-menu-horizontal > .ant-menu-item > a:hover':
case '.ant-menu-horizontal>.ant-menu-item>a:hover':
return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover'
default :
return selector
}
}
}
const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(
themePluginOption)
module.exports = createThemeColorReplacerPlugin
@@ -0,0 +1,115 @@
export default {
theme: [
{
key: 'dark',
fileName: 'dark.css',
theme: 'dark'
},
{
key: '#F5222D',
fileName: '#F5222D.css',
modifyVars: {
'@primary-color': '#F5222D'
}
},
{
key: '#FA541C',
fileName: '#FA541C.css',
modifyVars: {
'@primary-color': '#FA541C'
}
},
{
key: '#FAAD14',
fileName: '#FAAD14.css',
modifyVars: {
'@primary-color': '#FAAD14'
}
},
{
key: '#13C2C2',
fileName: '#13C2C2.css',
modifyVars: {
'@primary-color': '#13C2C2'
}
},
{
key: '#52C41A',
fileName: '#52C41A.css',
modifyVars: {
'@primary-color': '#52C41A'
}
},
{
key: '#2F54EB',
fileName: '#2F54EB.css',
modifyVars: {
'@primary-color': '#2F54EB'
}
},
{
key: '#722ED1',
fileName: '#722ED1.css',
modifyVars: {
'@primary-color': '#722ED1'
}
},
{
key: '#F5222D',
theme: 'dark',
fileName: 'dark-#F5222D.css',
modifyVars: {
'@primary-color': '#F5222D'
}
},
{
key: '#FA541C',
theme: 'dark',
fileName: 'dark-#FA541C.css',
modifyVars: {
'@primary-color': '#FA541C'
}
},
{
key: '#FAAD14',
theme: 'dark',
fileName: 'dark-#FAAD14.css',
modifyVars: {
'@primary-color': '#FAAD14'
}
},
{
key: '#13C2C2',
theme: 'dark',
fileName: 'dark-#13C2C2.css',
modifyVars: {
'@primary-color': '#13C2C2'
}
},
{
key: '#52C41A',
theme: 'dark',
fileName: 'dark-#52C41A.css',
modifyVars: {
'@primary-color': '#52C41A'
}
},
{
key: '#2F54EB',
theme: 'dark',
fileName: 'dark-#2F54EB.css',
modifyVars: {
'@primary-color': '#2F54EB'
}
},
{
key: '#722ED1',
theme: 'dark',
fileName: 'dark-#722ED1.css',
modifyVars: {
'@primary-color': '#722ED1'
}
}
]
}
@@ -0,0 +1,29 @@
为首屏增加 加载动画
====
## 需求
> 为了缓解用户第一次访问时,加载 JS 过大所导致用户等待白屏时间过长导致的用户体验不好,进行的一个优化动效。
## 实现方案
1. 将 动画加载 dom 元素放在 #app 内,Vue 生命周期开始时,会自动清掉 #app 下的所有元素。
2. 将 动画加载 dom 元素放在 body 下,Vue 生命周期开始时 App.vue (created, mounted) 调用
`@/utils/utll` 下的 removeLoadingAnimate(#id, timeout) 则会移除加载动画
最后一步:
​ 将样式插入到 `public/index.html` 文件的 `<head></head>` 最好写成内联 `<style>动画样式</style>`
----
目前提供有两个样式,均在 `public/loading` 文件夹内。且 pro 已经默认使用了一套 loading 动画方案,可以直接参考
`public/index.html`
## 写在最后
目前 pro 有页面 overflow 显示出浏览器滚动条时,页面会抖动一下的问题。
欢迎各位提供能解决的方案和实现 demo。如果在条件允许的情况下,建议请直接使用 pro 进行改造,也欢迎直接 PR
到 pro 的仓库
+19
View File
@@ -0,0 +1,19 @@
代码生成
====
## 需求
> 因代码生成由后端实现,所以若需要使用代码生成功能来生成前端代码的话,需要以下修改。
## 实现方案
1. 将本目录下`antdv`文件夹放入`RuoYi-Vue`中代码生成模块(ruoyi-generator)中`resource`的`vm`文件夹下;
2. 将本目录中`VelocityUtils.java`文件替换掉代码生成模块中`com.ruoyi.generator.util`的
`VelocityUtils.java`文件;
3. 重启后台项目,代码生成即由`element-ui`成功改为`ant design vue`。
![需替换及新增的位置](https://oss.fuzui.net/img/20210202004013.png)
@@ -0,0 +1,348 @@
package com.ruoyi.generator.util;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.constant.GenConstants;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.generator.domain.GenTable;
import com.ruoyi.generator.domain.GenTableColumn;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.apache.velocity.VelocityContext;
/**
* 模板处理工具类
*
* @author ruoyi
*/
public class VelocityUtils {
/** 项目空间路径 */
private static final String PROJECT_PATH = "main/java";
/** mybatis空间路径 */
private static final String MYBATIS_PATH = "main/resources/mapper";
/** 默认上级菜单,系统工具 */
private static final String DEFAULT_PARENT_MENU_ID = "3";
/**
* 设置模板变量信息
*
* @return 模板列表
*/
public static VelocityContext prepareContext(GenTable genTable) {
String moduleName = genTable.getModuleName();
String businessName = genTable.getBusinessName();
String packageName = genTable.getPackageName();
String tplCategory = genTable.getTplCategory();
String functionName = genTable.getFunctionName();
VelocityContext velocityContext = new VelocityContext();
velocityContext.put("tplCategory", genTable.getTplCategory());
velocityContext.put("tableName", genTable.getTableName());
velocityContext.put(
"functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】");
velocityContext.put("ClassName", genTable.getClassName());
velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName()));
velocityContext.put("moduleName", genTable.getModuleName());
velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName()));
velocityContext.put("businessName", genTable.getBusinessName());
velocityContext.put("basePackage", getPackagePrefix(packageName));
velocityContext.put("packageName", packageName);
velocityContext.put("author", genTable.getFunctionAuthor());
velocityContext.put("datetime", DateUtils.getDate());
velocityContext.put("pkColumn", genTable.getPkColumn());
velocityContext.put("importList", getImportList(genTable));
velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName));
velocityContext.put("columns", genTable.getColumns());
velocityContext.put("table", genTable);
velocityContext.put("dicts", getDicts(genTable));
setMenuVelocityContext(velocityContext, genTable);
if (GenConstants.TPL_TREE.equals(tplCategory)) {
setTreeVelocityContext(velocityContext, genTable);
}
if (GenConstants.TPL_SUB.equals(tplCategory)) {
setSubVelocityContext(velocityContext, genTable);
}
return velocityContext;
}
public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) {
String options = genTable.getOptions();
JSONObject paramsObj = JSONObject.parseObject(options);
String parentMenuId = getParentMenuId(paramsObj);
context.put("parentMenuId", parentMenuId);
}
public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) {
String options = genTable.getOptions();
JSONObject paramsObj = JSONObject.parseObject(options);
String treeCode = getTreecode(paramsObj);
String treeParentCode = getTreeParentCode(paramsObj);
String treeName = getTreeName(paramsObj);
context.put("treeCode", treeCode);
context.put("treeParentCode", treeParentCode);
context.put("treeName", treeName);
context.put("expandColumn", getExpandColumn(genTable));
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE));
}
if (paramsObj.containsKey(GenConstants.TREE_NAME)) {
context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME));
}
}
public static void setSubVelocityContext(VelocityContext context, GenTable genTable) {
GenTable subTable = genTable.getSubTable();
String subTableName = genTable.getSubTableName();
String subTableFkName = genTable.getSubTableFkName();
String subClassName = genTable.getSubTable().getClassName();
String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName);
context.put("subTable", subTable);
context.put("subTableName", subTableName);
context.put("subTableFkName", subTableFkName);
context.put("subTableFkClassName", subTableFkClassName);
context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName));
context.put("subClassName", subClassName);
context.put("subclassName", StringUtils.uncapitalize(subClassName));
context.put("subImportList", getImportList(genTable.getSubTable()));
}
/**
* 获取模板信息
*
* @return 模板列表
*/
public static List<String> getTemplateList(String tplCategory) {
List<String> templates = new ArrayList<String>();
templates.add("vm/java/domain.java.vm");
templates.add("vm/java/mapper.java.vm");
templates.add("vm/java/service.java.vm");
templates.add("vm/java/serviceImpl.java.vm");
templates.add("vm/java/controller.java.vm");
templates.add("vm/xml/mapper.xml.vm");
templates.add("vm/sql/sql.vm");
templates.add("vm/antdv/js/api.js.vm");
if (GenConstants.TPL_CRUD.equals(tplCategory) || GenConstants.TPL_TREE.equals(tplCategory)) {
templates.add("vm/antdv/index.vue.vm");
templates.add("vm/antdv/modules/CreateForm.vue.vm");
} else if (GenConstants.TPL_SUB.equals(tplCategory)) {
templates.add("vm/antdv/index.vue.vm");
templates.add("vm/antdv/modules/CreateForm.vue.vm");
templates.add("vm/antdv/modules/SubTable.vue.vm");
templates.add("vm/antdv/modules/CreateSubForm.vue.vm");
templates.add("vm/java/sub-domain.java.vm");
}
return templates;
}
/** 获取文件名 */
public static String getFileName(String template, GenTable genTable) {
// 文件名称
String fileName = "";
// 包路径
String packageName = genTable.getPackageName();
// 模块名
String moduleName = genTable.getModuleName();
// 大写类名
String className = genTable.getClassName();
// 业务名称
String businessName = genTable.getBusinessName();
String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/");
String mybatisPath = MYBATIS_PATH + "/" + moduleName;
String vuePath = "vue";
if (template.contains("domain.java.vm")) {
fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
}
if (template.contains("sub-domain.java.vm")
&& StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) {
fileName =
StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName());
} else if (template.contains("mapper.java.vm")) {
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
} else if (template.contains("service.java.vm")) {
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
} else if (template.contains("serviceImpl.java.vm")) {
fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className);
} else if (template.contains("controller.java.vm")) {
fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className);
} else if (template.contains("mapper.xml.vm")) {
fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className);
} else if (template.contains("sql.vm")) {
fileName = businessName + "Menu.sql";
} else if (template.contains("api.js.vm")) {
fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName);
} else if (template.contains("index.vue.vm")) {
fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
} else if (template.contains("CreateForm.vue.vm")) {
fileName =
StringUtils.format(
"{}/views/{}/{}/modules/CreateForm.vue", vuePath, moduleName, businessName);
} else if (template.contains("index-tree.vue.vm")) {
fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName);
} else if (template.contains("SubTable.vue.vm")) {
fileName =
StringUtils.format(
"{}/views/{}/{}/modules/SubTable.vue", vuePath, moduleName, businessName);
} else if (template.contains("CreateSubForm.vue.vm")) {
fileName =
StringUtils.format(
"{}/views/{}/{}/modules/CreateSubForm.vue", vuePath, moduleName, businessName);
}
System.out.println(fileName);
return fileName;
}
/**
* 获取包前缀
*
* @param packageName 包名称
* @return 包前缀名称
*/
public static String getPackagePrefix(String packageName) {
int lastIndex = packageName.lastIndexOf(".");
String basePackage = StringUtils.substring(packageName, 0, lastIndex);
return basePackage;
}
/**
* 根据列类型获取导入包
*
* @param genTable 业务表对象
* @return 返回需要导入的包列表
*/
public static HashSet<String> getImportList(GenTable genTable) {
List<GenTableColumn> columns = genTable.getColumns();
GenTable subGenTable = genTable.getSubTable();
HashSet<String> importList = new HashSet<String>();
if (StringUtils.isNotNull(subGenTable)) {
importList.add("java.util.List");
}
for (GenTableColumn column : columns) {
if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) {
importList.add("java.util.Date");
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
} else if (!column.isSuperColumn()
&& GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
importList.add("java.math.BigDecimal");
}
}
return importList;
}
/**
* 根据列类型获取字典组
*
* @param genTable 业务表对象
* @return 返回字典组
*/
public static String getDicts(GenTable genTable) {
List<GenTableColumn> columns = genTable.getColumns();
List<String> dicts = new ArrayList<String>();
for (GenTableColumn column : columns) {
if (!column.isSuperColumn()
&& StringUtils.isNotEmpty(column.getDictType())
&& StringUtils.equalsAny(
column.getHtmlType(),
new String[] {
GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX
})) {
dicts.add("'" + column.getDictType() + "'");
}
}
return StringUtils.join(dicts, ", ");
}
/**
* 获取权限前缀
*
* @param moduleName 模块名称
* @param businessName 业务名称
* @return 返回权限前缀
*/
public static String getPermissionPrefix(String moduleName, String businessName) {
return StringUtils.format("{}:{}", moduleName, businessName);
}
/**
* 获取上级菜单ID字段
*
* @param paramsObj 生成其他选项
* @return 上级菜单ID字段
*/
public static String getParentMenuId(JSONObject paramsObj) {
if (StringUtils.isNotEmpty(paramsObj)
&& paramsObj.containsKey(GenConstants.PARENT_MENU_ID)
&& StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) {
return paramsObj.getString(GenConstants.PARENT_MENU_ID);
}
return DEFAULT_PARENT_MENU_ID;
}
/**
* 获取树编码
*
* @param paramsObj 生成其他选项
* @return 树编码
*/
public static String getTreecode(JSONObject paramsObj) {
if (paramsObj.containsKey(GenConstants.TREE_CODE)) {
return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE));
}
return StringUtils.EMPTY;
}
/**
* 获取树父编码
*
* @param paramsObj 生成其他选项
* @return 树父编码
*/
public static String getTreeParentCode(JSONObject paramsObj) {
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE));
}
return StringUtils.EMPTY;
}
/**
* 获取树名称
*
* @param paramsObj 生成其他选项
* @return 树名称
*/
public static String getTreeName(JSONObject paramsObj) {
if (paramsObj.containsKey(GenConstants.TREE_NAME)) {
return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME));
}
return StringUtils.EMPTY;
}
/**
* 获取需要在哪一列上面显示展开按钮
*
* @param genTable 业务表对象
* @return 展开按钮列序号
*/
public static int getExpandColumn(GenTable genTable) {
String options = genTable.getOptions();
JSONObject paramsObj = JSONObject.parseObject(options);
String treeName = paramsObj.getString(GenConstants.TREE_NAME);
int num = 0;
for (GenTableColumn column : genTable.getColumns()) {
if (column.isList()) {
num++;
String columnName = column.getColumnName();
if (columnName.equals(treeName)) {
break;
}
}
}
return num;
}
}
@@ -0,0 +1,498 @@
<template>
<page-header-wrapper>
<a-card :bordered="false">
<!-- 条件搜索 -->
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="48">
#set($isInsert=0)
#set($isEdit=0)
#set($queryCount=0)
#set($querySpace="")
#foreach($column in $columns)
#if($column.insert)
#set($isInsert=1)
#end
#if($column.edit)
#set($isEdit=1)
#end
#if($column.query)
#set($queryCount=$queryCount+1)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($queryCount == 3)
<template v-if="advanced">
#set($querySpace=" ")
#end
#if($column.htmlType == "input")
${querySpace}
<a-col :md="8" :sm="24">
${querySpace}
<a-form-item label="${comment}" prop="${column.javaField}">
${querySpace}
<a-input v-model="queryParam.${column.javaField}"
placeholder="请输入${comment}" allow-clear/>
${querySpace}</a-form-item>
${querySpace}</a-col>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") &&
"" != $dictType)
${querySpace}
<a-col :md="8" :sm="24">
${querySpace}
<a-form-item label="${comment}" prop="${column.javaField}">
${querySpace}
<a-select placeholder="请选择${comment}"
v-model="queryParam.${column.javaField}" style="width: 100%"
allow-clear>
${querySpace}
<a-select-option v-for="(d, index) in ${column.javaField}Options"
:key="index" :value="d.dictValue">{{ d.dictLabel }}
</a-select-option>
${querySpace}</a-select>
${querySpace}</a-form-item>
${querySpace}</a-col>
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
${querySpace}
<a-col :md="8" :sm="24">
${querySpace}
<a-form-item label="${comment}" prop="${column.javaField}">
${querySpace}
<a-select placeholder="请选择${comment}"
v-model="queryParam.${column.javaField}" style="width: 100%"
allow-clear>
${querySpace}
<a-select-option>请选择字典生成</a-select-option>
${querySpace}</a-select>
${querySpace}</a-form-item>
${querySpace}</a-col>
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
${querySpace}
<a-col :md="8" :sm="24">
${querySpace}
<a-form-item label="${comment}" prop="${column.javaField}">
${querySpace}
<a-date-picker style="width: 100%" v-model="queryParam.${column.javaField}"
format="YYYY-MM-DD HH:mm:ss" allow-clear/>
${querySpace}</a-form-item>
${querySpace}</a-col>
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
${querySpace}
<a-col :md="8" :sm="24">
${querySpace}
<a-form-item label="${comment}">
${querySpace}
<a-range-picker style="width: 100%" v-model="daterange${AttrName}"
valueFormat="YYYY-MM-DD" format="YYYY-MM-DD" allow-clear/>
${querySpace}</a-form-item>
${querySpace}</a-col>
#end
#end
#end
#if($queryCount > 2)
</template>
#end
<a-col :md="!advanced && 8 || 24" :sm="24">
<span class="table-page-search-submitButtons"
:style="advanced && { float: 'right', overflow: 'hidden' } || {} ">
<a-button type="primary" @click="handleQuery"><a-icon type="search"/>查询</a-button>
<a-button style="margin-left: 8px" @click="resetQuery"><iot-icon
type="icon-refresh"/>重置</a-button>
#if($queryCount > 2)
<a @click="toggleAdvanced" style="margin-left: 8px">
{{ advanced ? '收起' : '展开' }}
<a-icon :type="advanced ? 'up' : 'down'"/>
</a>
#end
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- 操作 -->
<div class="table-operations">
#if($isInsert == 1)
<a-button type="primary" @click="$refs.createForm.handleAdd()"
v-hasPermi="['${moduleName}:${businessName}:add']">
<a-icon type="plus"/>
新增
</a-button>
#end
#if($isEdit == 1)
<a-button type="primary" :disabled="single"
@click="$refs.createForm.handleUpdate(undefined, ids)"
v-hasPermi="['${moduleName}:${businessName}:edit']">
<iot-icon type="icon-u-edit"/>
修改
</a-button>
#end
<a-button type="danger" :disabled="multiple" @click="handleDelete"
v-hasPermi="['${moduleName}:${businessName}:remove']">
<a-icon type="delete"/>
删除
</a-button>
<a-button type="primary" @click="handleExport"
v-hasPermi="['${moduleName}:${businessName}:export']">
<iot-icon type="icon-u-export"/>
导出
</a-button>
<a-button
type="dashed"
shape="circle"
:loading="loading"
:style="{float: 'right'}"
icon="reload"
@click="getList"/>
</div>
#if($isInsert == 1 || $isEdit == 1)
<!-- 增加修改 -->
<create-form
ref="createForm"
#foreach ($column in $columns)
#if(${column.dictType} != '')
:${column.javaField}Options="${column.javaField}Options"
#end
#end
#if($tplCategory == 'tree')
:${businessName}Options="${businessName}Options"
@select-tree="getTreeselect"
#end
@ok="getList"
/>
#end
<!-- 数据展示 -->
<a-table
:loading="loading"
:size="tableSize"
rowKey="${pkColumn.javaField}"
:columns="columns"
:data-source="list"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:pagination="false">
#foreach($column in $columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.list && $column.htmlType == "datetime")
<span slot="${javaField}" slot-scope="text, record">
{{ parseTime(record.${javaField}) }}
</span>
#elseif($column.list && "" != $column.dictType)
<span slot="${javaField}" slot-scope="text, record">
{{ ${javaField}Format(record) }}
</span>
#end
#end
<span slot="operation" slot-scope="text, record">
#if($isEdit == 1)
<a-divider type="vertical" v-hasPermi="['${moduleName}:${businessName}:edit']"/>
<a @click="$refs.createForm.handleUpdate(record, undefined)"
v-hasPermi="['${moduleName}:${businessName}:edit']">
<iot-icon type="icon-u-edit"/>修改
</a>
#end
#if($tplCategory == 'tree' && ($isInsert == 1 || $isEdit == 1))
<a-divider type="vertical" v-hasPermi="['${moduleName}:${businessName}:add']"/>
<a @click="$refs.createForm.handleAdd(record)"
v-hasPermi="['${moduleName}:${businessName}:add']">
<a-icon type="plus"/>新增
</a>
#end
<a-divider type="vertical" v-hasPermi="['${moduleName}:${businessName}:remove']"/>
<a @click="handleDelete(record)" v-hasPermi="['${moduleName}:${businessName}:remove']">
<a-icon type="delete"/>删除
</a>
</span>
</a-table>
#if($tplCategory != 'tree')
<!-- 分页 -->
<a-pagination
class="ant-table-pagination"
show-size-changer
show-quick-jumper
:current="queryParam.pageNum"
:total="total"
:page-size="queryParam.pageSize"
:showTotal="total => `共 ${total} 条`"
@showSizeChange="onShowSizeChange"
@change="changeSize"
/>
#end
</a-card>
</page-header-wrapper>
</template>
<script>
import {del${BusinessName}, list${BusinessName}} from '@/api/'
import CreateForm from './modules/CreateForm'
export default {
name: '${BusinessName}',
components: {
#if($isInsert == 1 || $isEdit == 1)
CreateForm
#end
},
data() {
return {
list: [],
selectedRowKeys: [],
selectedRows: [],
// 高级搜索 展开/关闭
advanced: false,
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
ids: [],
loading: false,
total: 0,
#if($tplCategory == 'tree' && ($isInsert == 1 || $isEdit == 1))
${businessName}Options: [],
#end
#foreach ($column in $columns)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if(${column.dictType} != '')
// $comment字典
${column.javaField}Options: [],
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
// $comment时间范围
daterange${AttrName}: [],
#end
#end
// 查询参数
queryParam: {
#foreach ($column in $columns)
#if($column.query)
$column.javaField: null,
#end
#end
pageNum: 1,
pageSize: 10
},
columns: [
#foreach($column in $columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk)
{
title: '${comment}',
dataIndex: '${javaField}',
ellipsis: true,
align: 'center'
},
#elseif($column.list && $column.htmlType == "datetime" || $column.list &&
"" != $column.dictType || $column.list && "" != $column.dictType)
{
title: '${comment}',
dataIndex: '${javaField}',
scopedSlots: {customRender: '${javaField}'},
ellipsis: true,
align: 'center'
},
#elseif($column.list && "" != $javaField)
{
title: '${comment}',
dataIndex: '${javaField}',
ellipsis: true,
align: 'center'
},
#end
#end
{
title: '操作',
dataIndex: 'operation',
width: '18%',
scopedSlots: {customRender: 'operation'},
align: 'center'
}
]
}
},
filters: {},
created() {
this.getList()
#foreach ($column in $columns)
#if(${column.dictType} != '')
this.getDicts('${column.dictType}').then(response => {
this.${column.javaField}Options = response.data
})
#end
#end
},
computed: {},
watch: {},
methods: {
/** 查询${functionName}列表 */
getList() {
this.loading = true
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
this.queryParam.params = {}
#break
#end
#end
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
if (this.daterange${AttrName} !== null && this.daterange${AttrName} !== ''
&& this.daterange${AttrName}.length !== 0) {
this.queryParam.params['begin${AttrName}'] = this.daterange${AttrName}[0]
this.queryParam.params['end${AttrName}'] = this.daterange${AttrName}[1]
}
#end
#end
list${BusinessName}(this.queryParam).then(response => {
#if($tplCategory == 'tree' && ($isInsert == 1 || $isEdit == 1))
this.list = this.handleTree(response.data, '${treeCode}', '${treeParentCode}')
#else
this.list = response.rows
#end
this.total = response.total
this.loading = false
})
},
#foreach ($column in $columns)
#if(${column.dictType} != '')
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
// $comment字典翻译
${column.javaField}Format(row, column) {
#if($column.htmlType == "checkbox")
if (row.${column.javaField} === null || row.${column.javaField} === undefined) {
return null
}
#end
return this.selectDictLabel#if($column.htmlType == "checkbox")s#end(
this.${column.javaField}Options, row.${column.javaField})
},
#end
#end
/** 搜索按钮操作 */
handleQuery() {
this.queryParam.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
#foreach ($column in $columns)
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
this.daterange${AttrName} = []
#end
#end
this.queryParam = {
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if(!($column.htmlType == "datetime" && $column.queryType == "BETWEEN"))
${column.javaField}: undefined,
#end
#end
#end
pageNum: 1,
pageSize: 10
}
this.handleQuery()
},
onShowSizeChange(current, pageSize) {
this.queryParam.pageSize = pageSize
this.getList()
},
changeSize(current, pageSize) {
this.queryParam.pageNum = current
this.queryParam.pageSize = pageSize
this.getList()
},
onSelectChange(selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
this.ids = this.selectedRows.map(item => item.${pkColumn.javaField})
this.single = selectedRowKeys.length !== 1
this.multiple = !selectedRowKeys.length
},
toggleAdvanced() {
this.advanced = !this.advanced
},
#if($tplCategory == 'tree' && ($isInsert == 1 || $isEdit == 1))
/** 查询菜单下拉树结构 */
getTreeselect() {
list${BusinessName}().then(response => {
this.${businessName}Options = []
const data = {${treeCode}: 0, ${treeName}: '主目录', children: []}
data.children = this.handleTree(response.data, '${treeCode}', '${treeParentCode}')
this.${businessName}Options.push(data)
})
},
#end
/** 删除按钮操作 */
handleDelete(row) {
var that = this
const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids
this.$confirm({
title: '确认删除所选中数据?',
content: '当前选中编号为' + ${pkColumn.javaField}s + '的数据',
onOk() {
return del${BusinessName}(${pkColumn.javaField}s)
.then(() => {
that.onSelectChange([], [])
that.getList()
that.$message.success(
'删除成功',
3
)
})
},
onCancel() {
}
})
},
/** 导出按钮操作 */
handleExport() {
var that = this
this.$confirm({
title: '是否确认导出?',
content: '此操作将导出当前条件下所有数据而非选中数据',
onOk() {
that.download('${moduleName}/${businessName}/export', {
...that.queryParam
}, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)
},
onCancel() {
}
})
}
}
}
</script>
@@ -0,0 +1,44 @@
import request from '@/utils/request'
// 查询${functionName}列表
export function list${BusinessName} (query) {
return request({
url: '/${moduleName}/${businessName}/list',
method: 'get',
params: query
})
}
// 查询${functionName}详细
export function get${BusinessName} (${pkColumn.javaField}) {
return request({
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
method: 'get'
})
}
// 新增${functionName}
export function add${BusinessName} (data) {
return request({
url: '/${moduleName}/${businessName}',
method: 'post',
data: data
})
}
// 修改${functionName}
export function update${BusinessName} (data) {
return request({
url: '/${moduleName}/${businessName}',
method: 'put',
data: data
})
}
// 删除${functionName}
export function del${BusinessName} (${pkColumn.javaField}) {
return request({
url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField},
method: 'delete'
})
}
@@ -0,0 +1,424 @@
#set($isInsert=0)
#set($isEdit=0)
#set($hasRichText=0)
#foreach($column in $columns)
#if($column.insert)
#set($isInsert=1)
#end
#if($column.edit)
#set($isEdit=1)
#end
#if($isInsert == 1 && $isEdit == 1)
#break
#end
#end
#if($isInsert == 0 && $isEdit == 0)
<template>
<a-result title="多余文件,请删除">
<template #icon>
<a-icon type="smile" theme="twoTone"/>
</template>
<template #extra>
<a-button type="primary" @click="returnIndex">
返回首页
</a-button>
</template>
</a-result>
</template>
#stop
#end
<template>
<a-drawer width="35%" :label-col="4" :wrapper-col="14" :visible="open" @close="onClose">
<a-divider orientation="left">
<b>{{ formTitle }}</b>
</a-divider>
<a-form-model ref="form" :model="form" :rules="rules">
#set($requiredCount = 0)
#set($ruleLastIndex = 0)
#foreach($column in $columns)
#if(${column.dictType} != '')
#set($ruleLastIndex = $foreach.count)
#end
#set($field=$column.javaField)
#if(($column.insert || $column.edit) && !$column.pk)
#if(($column.usableColumn) || (!$column.superColumn))
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.required)
#set($requiredCount = $requiredCount + 1)
#end
#set($dictType=$column.dictType)
<a-form-model-item label="${comment}" prop="${field}"
#if($column.insert && !$column.edit)v-if="formType === 1"#end#if(!$column.insert && $column.edit)v-if="formType === 2"#end>
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
<a-tree-select
v-model="form.${field}"
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:tree-data="${businessName}Options"
placeholder="请选择"
:replaceFields="{children:'children', title:'${treeName}', key:'${treeCode}', value: '${treeCode}' }"
tree-default-expand-all
>
</a-tree-select>
#elseif($column.htmlType == "input")
<a-input v-model="form.${field}" placeholder="请输入${comment}"/>
#elseif($column.htmlType == "imageUpload")
<file-upload v-model="form.${field}" type="image"></file-upload>
#elseif($column.htmlType == "fileUpload")
<file-upload v-model="form.${field}" type="file"></file-upload>
#elseif($column.htmlType == "editor")
#set($hasRichText=1)
<editor ref="${field}Editor" v-model="form.${field}"/>
#elseif($column.htmlType == "select" && "" != $dictType)
<a-select placeholder="请选择${comment}" v-model="form.${field}">
<a-select-option v-for="(d, index) in ${field}Options" :key="index"
:value="d.dictValue">{{ d.dictLabel }}
</a-select-option>
</a-select>
#elseif($column.htmlType == "select" && $dictType)
<a-select placeholder="请选择${comment}" v-model="form.${field}">
<a-select-option value="">请选择字典生成</a-select-option>
</a-select>
#elseif($column.htmlType == "checkbox" && "" != $dictType)
<a-select
mode="multiple"
v-model="form.${field}"
placeholder="请选择${comment}"
>
<a-select-option v-for="(d, index) in ${field}Options" :key="index"
:value="d.dictValue">{{ d.dictLabel }}
</a-select-option>
</a-select>
#elseif($column.htmlType == "checkbox" && $dictType)
<a-select placeholder="请选择${comment}" v-model="form.${field}">
<a-select-option value="">请选择字典生成</a-select-option>
</a-select>
#elseif($column.htmlType == "radio" && "" != $dictType)
<a-radio-group v-model="form.${field}" button-style="solid">
<a-radio-button v-for="(d, index) in ${field}Options" :key="index"
:value="d.dictValue">{{ d.dictLabel }}
</a-radio-button>
</a-radio-group>
#elseif($column.htmlType == "radio" && $dictType)
<a-radio-group v-model="form.${field}" button-style="solid">
<a-radio-button value="">请选择字典生成</a-radio-button>
</a-radio-group>
#elseif($column.htmlType == "datetime")
<a-date-picker style="width: 100%" v-model="form.${field}"
format="YYYY-MM-DD HH:mm:ss" allow-clear/>
#elseif($column.htmlType == "textarea")
<a-input v-model="form.${field}" placeholder="请输入内容" type="textarea"
allow-clear/>
#end
</a-form-model-item>
#end
#end
#end
#if($table.sub)
<a-divider orientation="left">
<b>${subTable.functionName}</b>
</a-divider>
<sub-table ref="subTable" :list="subList" @add="handleAdd${subclassName}"
@delete="handleDelete${subclassName}"/>
<p>当前存在 {{ subList.length }} 条信息</p>
<a-button type="dashed" @click="$refs.subTable.show()">
详情
</a-button>
<br>
#end
<div class="bottom-control">
<a-space>
<a-button type="primary" @click="submitForm">
保存
</a-button>
<a-button type="dashed" @click="cancel">
取消
</a-button>
</a-space>
</div>
</a-form-model>
</a-drawer>
</template>
<script>
import {add${BusinessName}, get${BusinessName}, update${BusinessName}} from '@/api/'
import SubTable from './SubTable'
import Editor from '@/components/Editor'
#if($table.sub)
#end
#if($hasRichText == 1)
#end
export default {
name: 'CreateForm',
props: {
#set($propsCount = 0)
#foreach ($column in $columns)
#if(${column.dictType} != '')
#set($propsCount = 1)
${column.javaField}Options: {
type: Array,
required: true
#if($foreach.count!=$ruleLastIndex || $tplCategory == 'tree')
},
#else
}
#end
#end
#end
#if($tplCategory == 'tree')
${businessName}Options: {
type: Array,
required: true
}
#end
}
,
components: {
#if($table.sub)
SubTable#if($hasRichText == 1),#end
#end
#if($hasRichText == 1)
Editor
#end
}
,
data()
{
return {
loading: false,
#if($table.sub)
subList: [],
#end
formTitle: '',
#foreach ($column in $columns)
#if($column.insert || $column.edit)
#if($column.htmlType == "editor")
${column.javaField}Editor: '',
#end
#end
#end
// 表单参数
form: {
#foreach ($column in $columns)
#if($column.insert || $column.edit)
#if($column.htmlType == "radio")
$column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0
#else'0'#end#if($foreach.count != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
#end
},
// 1增加,2修改
formType: 1,
open: false,
rules: {
#set($currentRequiredCount = 1)
#foreach ($column in $columns)
#if(($column.insert || $column.edit) && !$column.pk)
#if(($column.usableColumn) || (!$column.superColumn))
#if($column.required)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
$column.javaField: [
{
required: true, message: '$comment不能为空', trigger: #if($column.htmlType ==
"select")'change'#else'blur'#end }
]#if($currentRequiredCount != $requiredCount),#end
#set($currentRequiredCount = $currentRequiredCount + 1)
#end
#end
#end
#end
}
}
}
,
filters: {
}
,
created()
{
}
,
computed: {
}
,
watch: {
}
,
mounted()
{
}
,
methods: {
onClose()
{
this.open = false
#if($table.sub)
this.subList = []
#end
}
,
// 取消按钮
cancel()
{
this.open = false
this.reset()
#if($table.sub)
this.subList = []
#end
}
,
// 表单重置
reset()
{
this.formType = 1
this.form = {
#foreach ($column in $columns)
#if($column.insert || $column.edit)
#if($column.htmlType == "radio")
$column.javaField: #if($column.javaType == "Integer" || $column.javaType == "Long")0
#else'0'#end#if($foreach.count != $columns.size()),#end
#elseif($column.htmlType == "checkbox")
$column.javaField: []#if($foreach.count != $columns.size()),#end
#else
$column.javaField: null#if($foreach.count != $columns.size()),#end
#end
#end
#end
}
}
,
/** 新增按钮操作 */
handleAdd(row)
{
this.reset()
#if($tplCategory == 'tree')
this.$emit('select-tree')
if (row != null && row.${treeCode}) {
this.form.${treeParentCode} = row.${treeCode}
} else {
this.form.${treeParentCode} = 0
}
#end
this.formType = 1
this.open = true
this.formTitle = '添加'
}
,
#if($table.sub)
/** 新增子项操作 */
handleAdd${subclassName} (row)
{
this.subList.push(row)
}
,
/** 删除子项操作 */
handleDelete${subclassName} (index, indexs)
{
if (index !== undefined) {
this.subList.splice(index, 1)
return
}
indexs.forEach(item => {
this.subList.splice(item, 1)
})
}
,
#end
/** 修改按钮操作 */
handleUpdate(row, ids)
{
this.reset()
#if($tplCategory == 'tree')
this.$emit('select-tree')
#end
this.formType = 2
const ${pkColumn.javaField} = row ? row.${pkColumn.javaField} : ids
get${BusinessName}(${pkColumn.javaField}).then(response => {
this.form = response.data
#if($table.sub)
this.subList = response.data.${subclassName}List
#end
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
if (this.form.$column.javaField !== null) {
this.form.$column.javaField = this.form.${column.javaField}.split(',')
} else {
this.form.$column.javaField = undefined
}
#end
#end
this.open = true
this.formTitle = '修改'
})
}
,
/** 提交按钮 */
submitForm: function () {
#foreach ($column in $columns)
#if($column.insert || $column.edit)
#if($column.htmlType == "editor")
this.form.${column.javaField} = this.${column.javaField}Editor.getValue()
#end
#end
#end
this.
#
[[$]]
#refs.form.validate(valid => {
if (valid) {
#foreach ($column in $columns)
#if($column.htmlType == "checkbox")
this.form.$column.javaField = this.form.${column.javaField}.join(',')
#end
#end
#if($table.sub)
this.form.${subclassName}List = this.subList
#end
if (this.form.${pkColumn.javaField} !== undefined && this.form.${pkColumn.javaField}
!== null) {
update${BusinessName}(this.form).then(response => {
this.$message.success(
'修改成功',
3
)
this.open = false
this.$emit('ok')
})
} else {
add${BusinessName}(this.form).then(response => {
this.$message.success(
'新增成功',
3
)
this.open = false
this.$emit('ok')
})
}
} else {
return false
}
})
}
}
}
</script>
@@ -0,0 +1,101 @@
<template>
<!-- 增加修改 -->
<a-drawer width="35%" :label-col="4" :wrapper-col="14" :visible="open" @close="onClose">
<a-divider orientation="left">
<b>${subTable.functionName}</b>
</a-divider>
<a-form-model ref="form" :model="form">
#set($columnLastIndex = 0)
#foreach($column in $subTable.columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
#set($columnLastIndex = $foreach.count)
<a-form-model-item label="${comment}" prop="${javaField}">
<a-input v-model="form.${javaField}" placeholder="请输入"/>
</a-form-model-item>
#end
#end
<div class="bottom-control">
<a-space>
<a-button type="primary" @click="submitForm">
保存
</a-button>
<a-button type="dashed" @click="cancel">
取消
</a-button>
</a-space>
</div>
</a-form-model>
</a-drawer>
</template>
<script>
export default {
name: 'CreateSubForm',
props: {},
components: {},
data() {
return {
formTitle: '增加',
// 表单参数
form: {
#foreach($column in $subTable.columns)
#set($javaField=$column.javaField)
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
${$javaField}: undefined#if($columnLastIndex != $foreach.count),#end
#end
#end
},
open: false
}
},
filters: {},
created() {
},
computed: {},
watch: {},
methods: {
onClose() {
this.open = false
},
// 取消按钮
cancel() {
this.open = false
this.reset()
},
// 表单重置
reset() {
this.form = {
#foreach($column in $subTable.columns)
#set($javaField=$column.javaField)
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
${$javaField}: undefined#if($columnLastIndex != $foreach.count),#end
#end
#end
}
},
/** 新增按钮操作 */
handleAdd() {
this.open = true
this.reset()
},
/** 提交按钮 */
submitForm: function () {
this.$emit('add', this.form)
this.open = false
}
}
}
</script>
@@ -0,0 +1,152 @@
<template>
<a-modal
ref="subTableModel"
:title="'${subTable.functionName}'"
width="80%"
:visible="visible"
@cancel="close"
>
<template slot="footer">
<a-button type="dashed" @click="close">
关闭
</a-button>
</template>
<a-card :bordered="false">
<!-- 操作 -->
<div class="table-operations">
<a-button type="primary" @click="$refs.createSubForm.handleAdd()">
<a-icon type="plus"/>
新增
</a-button>
<a-button type="danger" :disabled="multiple" @click="handleDelete">
<a-icon type="delete"/>
删除
</a-button>
</div>
<!-- 增加修改 -->
<create-sub-form
ref="createSubForm"
@add="handleAdd${subClassName}"
/>
<!-- 数据展示 -->
<a-table
:loading="loading"
:size="tableSize"
rowKey="${subTableFkclassName}"
:columns="columns"
:data-source="list"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:pagination="false">
<span slot="operation" slot-scope="text, record">
<a-divider type="vertical"/>
<a @click="handleDelete(record)">
<a-icon type="delete"/>删除
</a>
</span>
</a-table>
</a-card>
</a-modal>
</template>
<script>
import CreateSubForm from './CreateSubForm'
export default {
name: 'SubTable',
components: {
CreateSubForm
},
props: {
list: {
type: Array,
required: true
}
},
data() {
return {
visible: false,
selectedRowKeys: [],
selectedRows: [],
// 高级搜索 展开/关闭
advanced: false,
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
ids: [],
loading: false,
columns: [
#foreach($column in $subTable.columns)
#set($javaField=$column.javaField)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.pk || $javaField == ${subTableFkclassName})
#elseif($column.list && "" != $javaField)
{
title: '${comment}',
dataIndex: '${javaField}',
ellipsis: true,
align: 'center'
},
#end
#end
{
title: '操作',
dataIndex: 'operation',
width: '18%',
scopedSlots: {customRender: 'operation'},
align: 'center'
}
]
}
},
filters: {},
created() {
},
computed: {},
watch: {},
methods: {
// 关闭模态框
close() {
this.visible = false
this.selectedRowKeys = []
this.selectedRows = []
},
// 打开抽屉(由外面的组件调用)
show() {
this.visible = true
},
onSelectChange(selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = selectedRows
this.ids = selectedRowKeys.map(item => item - 1)
this.single = selectedRowKeys.length !== 1
this.multiple = !selectedRowKeys.length
},
/** 删除按钮操作 */
handleDelete(row) {
var that = this
if (row) {
var index = (this.list || []).findIndex((item) => item === row)
}
const ids = index !== undefined ? index : this.ids
this.$confirm({
title: '确认删除所选中数据?',
content: '当前选中序号为' + ids + '的数据',
onOk() {
that.$emit('delete', index, ids)
},
onCancel() {
}
})
},
handleAdd${subClassName} (row) {
this.$emit('add', row)
}
}
}
</script>
@@ -0,0 +1,39 @@
先增加依赖
```bash
// npm
$ npm install --save-dev webpack-bundle-analyzer
// or yarn
$ yarn add webpack-bundle-analyzer -D
```
配置文件 `vue.config.js` 增加 `configureWebpack.plugins` 参数
```
const path = require('path')
const webpack = require('webpack')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
function resolve (dir) {
return path.join(__dirname, dir)
}
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
// Ignore all locale files of moment.js
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
// 依赖大小分析工具
new BundleAnalyzerPlugin(),
]
},
...
}
```
启动 `cli` 的 `build` 命令进行项目编译,编译完成时,会自动运行一个 http://localhost:8888
的地址,完整显示了支持库依赖
+23
View File
@@ -0,0 +1,23 @@
module.exports = {
moduleFileExtensions: [
'js',
'jsx',
'json',
'vue'
],
transform: {
'^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
testURL: 'http://localhost/'
}
+18
View File
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "es6",
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
},
"exclude": [
"node_modules",
"dist"
],
"include": [
"src/**/*"
]
}
+83
View File
@@ -0,0 +1,83 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# 基本设置
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# API 代理到后端服务
location /api/ {
proxy_pass http://backend:9092/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
# 健康检查
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 前端路由支持
location / {
try_files $uri $uri/ /index.html;
}
# 错误页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
+87
View File
@@ -0,0 +1,87 @@
{
"name": "universal",
"version": "1.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint",
"dev": "vue-cli-service serve",
"start": "vue-cli-service serve"
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"ant-design-vue": "^1.7.8",
"axios": "^1.10.0",
"codemirror": "^5.65.19",
"concurrently": "^7.0.0",
"core-js": "^3.19.2",
"echarts": "^5.2.2",
"ezuikit-flv": "^1.0.3-alpha.1",
"file-saver": "^2.0.5",
"highlight.js": "^11.3.1",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"lodash.clonedeep": "^4.5.0",
"lodash.get": "^4.4.2",
"lodash.pick": "^4.4.0",
"md5": "^2.3.0",
"mockjs2": "^1.0.8",
"moment": "^2.29.1",
"nprogress": "^0.2.0",
"qs": "^6.10.1",
"screenfull": "5.0.2",
"sql-formatter": "^4.0.2",
"store": "^2.0.12",
"vue": "^2.6.11",
"vue-clipboard2": "^0.3.3",
"vue-codemirror": "^4.0.6",
"vue-container-query": "^0.1.0",
"vue-copy-to-clipboard": "^1.0.3",
"vue-count-to": "^1.0.13",
"vue-cropper": "0.5.8",
"vue-i18n": "^8.24.2",
"vue-json-viewer": "^2.2.21",
"vue-resizable": "2.1.3",
"vue-router": "^3.2.0",
"vue-svg-component-runtime": "^1.0.1",
"vuex": "^3.4.0",
"wangeditor": "^4.7.10"
},
"devDependencies": {
"@ant-design/colors": "^6.0.0",
"@babel/eslint-parser": "^7.27.5",
"@vue/cli-plugin-babel": "^4.0.4",
"@vue/cli-plugin-router": "^4.0.4",
"@vue/cli-plugin-unit-jest": "^4.0.4",
"@vue/cli-plugin-vuex": "^4.0.4",
"@vue/cli-service": "^4.0.4",
"@vue/eslint-config-standard": "^4.0.0",
"@vue/test-utils": "^1.3.0",
"babel-plugin-import": "^1.12.2",
"babel-plugin-transform-remove-console": "^6.9.4",
"eslint": "^8.57.1",
"eslint-plugin-html": "^5.0.0",
"eslint-plugin-vue": "^8.7.1",
"git-revision-webpack-plugin": "^3.0.6",
"husky": "^7.0.4",
"less": "^3.13.1",
"less-loader": "^7.3.0",
"lint-staged": "^12.1.3",
"opencollective": "^1.0.3",
"opencollective-postinstall": "^2.0.2",
"vue-svg-icon-loader": "^2.1.1",
"vue-template-compiler": "^2.7.16",
"webpack": "^4.46.0",
"webpack-theme-color-replacer": "^1.3.12"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,vue,md,jsx,css}": "eslint --cache --fix"
}
}
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
plugins: {
autoprefixer: {}
}
}
File diff suppressed because it is too large Load Diff
Binary file not shown.
+439
View File
@@ -0,0 +1,439 @@
/*//////////////////////////////////////////////////////////////////
[ FONT ]*/
@font-face {
font-family: Poppins-Regular;
src: url('../fonts/poppins/Poppins-Regular.ttf');
}
@font-face {
font-family: Poppins-Medium;
src: url('../fonts/poppins/Poppins-Medium.ttf');
}
@font-face {
font-family: Poppins-Bold;
src: url('../fonts/poppins/Poppins-Bold.ttf');
}
@font-face {
font-family: Poppins-SemiBold;
src: url('../fonts/poppins/Poppins-SemiBold.ttf');
}
.limiter {
width: 100%;
margin: 0 auto;
font-family: Poppins-Regular, sans-serif;
}
.container-login100 {
width: 100%;
/* min-height: 100vh; */
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
background: #f2f2f2;
}
.wrap-login100 {
background: #fff;
border-radius: 10px;
overflow: hidden;
padding: 77px 55px 33px 55px;
box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
-moz-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
-webkit-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
-o-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
-ms-box-shadow: 0 5px 10px 0px rgba(0, 0, 0, 0.1);
}
/*------------------------------------------------------------------
[ Form ]*/
.login100-form {
width: 100%;
}
.login100-form-title {
display: block;
font-family: Poppins-Bold;
font-size: 30px;
color: #333333;
line-height: 1.2;
text-align: center;
}
.login100-form-title i {
font-size: 60px;
}
/*------------------------------------------------------------------
[ Input ]*/
.wrap-input100 {
width: 100%;
position: relative;
border-bottom: 2px solid #adadad;
margin-bottom: 37px;
}
.input100 {
font-family: Poppins-Regular;
font-size: 15px;
color: #555555;
line-height: 1.2;
display: block;
width: 100%;
height: 45px;
background: transparent;
padding: 0 5px;
outline: none;
border: none;
}
.input100::-webkit-input-placeholder {
color: #adadad;
}
.input100:-moz-placeholder {
color: #adadad;
}
.input100::-moz-placeholder {
color: #adadad;
}
.input100:-ms-input-placeholder {
color: #adadad;
}
/*---------------------------------------------*/
.focus-input100 {
position: absolute;
display: block;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
color: transparent;
}
.focus-input100::before {
content: "";
display: block;
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
-webkit-transition: all 0.4s;
-o-transition: all 0.4s;
-moz-transition: all 0.4s;
transition: all 0.4s;
background: #6a7dfe;
background: -webkit-linear-gradient(left, #21d4fd, #b721ff);
background: -o-linear-gradient(left, #21d4fd, #b721ff);
background: -moz-linear-gradient(left, #21d4fd, #b721ff);
background: linear-gradient(left, #21d4fd, #b721ff);
}
.focus-input100::after {
font-family: Poppins-Regular;
font-size: 15px;
color: #999999;
line-height: 1.2;
content: attr(data-placeholder);
display: block;
width: 100%;
position: absolute;
top: 16px;
left: 0px;
padding-left: 5px;
-webkit-transition: all 0.4s;
-o-transition: all 0.4s;
-moz-transition: all 0.4s;
transition: all 0.4s;
}
.input100:focus + .focus-input100::after {
top: -15px;
}
.input100:focus + .focus-input100::before {
width: 100%;
}
.has-val.input100 + .focus-input100::after {
top: -15px;
}
.has-val.input100 + .focus-input100::before {
width: 100%;
}
/*---------------------------------------------*/
.btn-show-pass {
font-size: 15px;
color: #999999;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
align-items: center;
position: absolute;
height: 100%;
top: 0;
right: 0;
padding-right: 5px;
cursor: pointer;
-webkit-transition: all 0.4s;
-o-transition: all 0.4s;
-moz-transition: all 0.4s;
transition: all 0.4s;
}
.btn-show-pass:hover {
color: #6a7dfe;
color: -webkit-linear-gradient(left, #21d4fd, #b721ff);
color: -o-linear-gradient(left, #21d4fd, #b721ff);
color: -moz-linear-gradient(left, #21d4fd, #b721ff);
color: linear-gradient(left, #21d4fd, #b721ff);
}
.btn-show-pass.active {
color: #6a7dfe;
color: -webkit-linear-gradient(left, #21d4fd, #b721ff);
color: -o-linear-gradient(left, #21d4fd, #b721ff);
color: -moz-linear-gradient(left, #21d4fd, #b721ff);
color: linear-gradient(left, #21d4fd, #b721ff);
}
/*------------------------------------------------------------------
[ Button ]*/
.container-login100-form-btn {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
flex-wrap: wrap;
justify-content: center;
padding-top: 13px;
}
.wrap-login100-form-btn {
width: 100%;
display: block;
position: relative;
z-index: 1;
border-radius: 25px;
overflow: hidden;
margin: 0 auto;
}
.login100-form-bgbtn {
position: absolute;
z-index: -1;
width: 300%;
height: 100%;
background: #a64bf4;
background: -webkit-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
background: -o-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
background: -moz-linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
background: linear-gradient(right, #21d4fd, #b721ff, #21d4fd, #b721ff);
top: 0;
left: -100%;
-webkit-transition: all 0.4s;
-o-transition: all 0.4s;
-moz-transition: all 0.4s;
transition: all 0.4s;
}
.login100-form-btn {
font-family: Poppins-Medium;
font-size: 15px;
color: #fff;
line-height: 1.2;
text-transform: uppercase;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -ms-flexbox;
display: flex;
justify-content: center;
align-items: center;
padding: 0 20px;
width: 100%;
height: 50px;
outline: none !important;
border: none;
background: transparent;
}
.login100-form-btn:hover {
cursor: pointer;
}
.wrap-login100-form-btn:hover .login100-form-bgbtn {
left: 0;
}
/*------------------------------------------------------------------
[ Responsive ]*/
@media (max-width: 576px) {
.wrap-login100 {
padding: 77px 15px 33px 15px;
}
}
/*------------------------------------------------------------------
[ Alert validate ]*/
.validate-input {
position: relative;
}
.alert-validate::before {
content: attr(data-validate);
position: absolute;
max-width: 70%;
background-color: #fff;
border: 1px solid #c80000;
border-radius: 2px;
padding: 4px 25px 4px 10px;
top: 50%;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-o-transform: translateY(-50%);
transform: translateY(-50%);
right: 0px;
pointer-events: none;
font-family: Poppins-Regular;
color: #c80000;
font-size: 13px;
line-height: 1.4;
text-align: left;
visibility: hidden;
opacity: 0;
-webkit-transition: opacity 0.4s;
-o-transition: opacity 0.4s;
-moz-transition: opacity 0.4s;
transition: opacity 0.4s;
}
.alert-validate::after {
content: "\f06a";
font-family: FontAwesome;
font-size: 16px;
color: #c80000;
display: block;
position: absolute;
background-color: #fff;
top: 50%;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
-o-transform: translateY(-50%);
transform: translateY(-50%);
right: 5px;
}
.alert-validate:hover:before {
visibility: visible;
opacity: 1;
}
@media (max-width: 992px) {
.alert-validate::before {
visibility: visible;
opacity: 1;
}
}
/** util **/
.p-b-26 {
padding-bottom: 26px;
}
.p-b-48 {
padding-bottom: 48px;
}
.p-t-115 {
padding-top: 115px;
}
@keyframes login {
from {
width: 100%
}
to {
width: 50px
}
}
.login-loading {
width: 50px;
animation-name: login;
animation-duration: 0.5s;
}
.login-loading > .login100-form-btn {
visibility: hidden !important;
}
.login-loading-class {
stroke: rgb(255, 255, 255) !important;
background-color: transparent !important;
}
.login-loading-class > .el-loading-spinner .path {
stroke: rgb(255, 255, 255) !important;
}
/**font**/
/* .zmdi {
display: inline-block;
font: normal normal normal 14px/1 'Material-Design-Iconic-Font';
font-size: 14px;
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.zmdi-font::before {
content: '\f16a';
}
.login100-form-title i {
font-size: 60px;
} */
@@ -0,0 +1,8 @@
Place EZVIZ decoder assets here if you use custom decoder path.
- Example: decoder.js and related wasm files
- Then set decoder to "/ezviz/decoder.js" in EzuikitFlv options
Docs:
- https://open.ys7.com/help/1812
- https://ezviz-openbiz.github.io/EZUIKit-flv/global.html#FlvOptions
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
class AudioProcessor extends AudioWorkletProcessor{constructor(){super(),this.accumulatedData=new Float32Array(0)}process(t,a,s){const e=t[0]?.[0];if(e){const t=new Float32Array(this.accumulatedData.length+e.length);t.set(this.accumulatedData),t.set(e,this.accumulatedData.length),this.accumulatedData=t,this.accumulatedData.length>=4096&&(this.port.postMessage(this.accumulatedData.slice(0,4096)),this.accumulatedData=this.accumulatedData.slice(4096))}return!0}}registerProcessor("audio-processor",AudioProcessor);
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
var Module={};function threadPrintErr(){var e=Array.prototype.slice.call(arguments).join(" ");console.error(e)}function threadAlert(){var e=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:e,threadId:Module._pthread_self()})}var err=threadPrintErr;function moduleLoaded(){}this.alert=threadAlert,Module.instantiateWasm=function(e,a){var t=new WebAssembly.Instance(Module.wasmModule,e);return Module.wasmModule=null,a(t),t.exports},this.onmessage=function(e){try{if("load"===e.data.cmd){if(Module.wasmModule=e.data.wasmModule,Module.wasmMemory=e.data.wasmMemory,Module.buffer=Module.wasmMemory.buffer,Module.ENVIRONMENT_IS_PTHREAD=!0,"string"===typeof e.data.urlOrBlob)importScripts(e.data.urlOrBlob);else{var a=URL.createObjectURL(e.data.urlOrBlob);importScripts(a),URL.revokeObjectURL(a)}moduleLoaded()}else if("objectTransfer"===e.data.cmd)Module.PThread.receiveObjectTransfer(e.data);else if("run"===e.data.cmd){Module.__performance_now_clock_drift=performance.now()-e.data.time,Module.__emscripten_thread_init(e.data.threadInfoStruct,0,0);var t=e.data.stackBase,r=e.data.stackBase+e.data.stackSize;Module.establishStackSpace(r,t),Module._emscripten_tls_init(),Module.PThread.receiveObjectTransfer(e.data),Module.PThread.setThreadStatus(Module._pthread_self(),1);try{var d=Module.invokeEntryPoint(e.data.start_routine,e.data.arg);Module.getNoExitRuntime()||Module.PThread.threadExit(d)}catch(e){if("Canceled!"===e)Module.PThread.threadCancel();else if("unwind"!=e){if(!(e instanceof Module.ExitStatus))throw Module.PThread.threadExit(-2),e;Module.getNoExitRuntime()||Module.PThread.threadExit(e.status)}}}else"cancel"===e.data.cmd?Module._pthread_self()&&Module.PThread.threadCancel():"setimmediate"===e.data.target||("processThreadQueue"===e.data.cmd?Module._pthread_self()&&Module._emscripten_current_thread_process_queued_calls():(err("worker.js received unknown command "+e.data.cmd),err(e.data)))}catch(e){throw err("worker.js onmessage() captured an uncaught exception: "+e),e&&e.stack&&err(e.stack),e}};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+151
View File
@@ -0,0 +1,151 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
<link rel="icon" href="favicon.ico"/>
<!-- jessibuca begin -->
<link rel="stylesheet" type="text/css" href="/css/iconfont.css">
<link rel="stylesheet" type="text/css" href="/css/login.css">
<!-- jessibuca end -->
<title>IoT Universal</title>
<style>
.first-loading-wrp {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
min-height: 420px;
height: 100%;
}
.first-loading-wrp > h1 {
font-size: 128px;
}
.first-loading-wrp .loading-wrp {
padding: 98px;
display: flex;
justify-content: center;
align-items: center;
}
.dot {
animation: antRotate 1.2s infinite linear;
transform: rotate(45deg);
position: relative;
display: inline-block;
font-size: 32px;
width: 32px;
height: 32px;
box-sizing: border-box;
}
.dot i {
width: 14px;
height: 14px;
position: absolute;
display: block;
background-color: #1890ff;
border-radius: 100%;
transform: scale(0.75);
transform-origin: 50% 50%;
opacity: 0.3;
animation: antSpinMove 1s infinite linear alternate;
}
.dot i:nth-child(1) {
top: 0;
left: 0;
}
.dot i:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.dot i:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.dot i:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s;
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@keyframes antSpinMove {
to {
opacity: 1;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1;
}
}
.amap-logo,
.amap-copyright {
display: none !important;
}
</style>
<!-- require cdn assets css -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>"/>
<% } %>
</head>
<body>
<noscript>
<strong>请启用JavaScript后再使用IoT Platform</strong>
</noscript>
<script type="text/javascript">
// window._AMapSecurityConfig = {
// serviceHost: 'https://map.universal.cn/_AMapService',
// }
window._AMapSecurityConfig = {
securityJsCode: "139244dd5f5f4dec671214637f3f83bd",
};
</script>
<div id="app">
<div class="first-loading-wrp">
<div class="loading-wrp">
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
</div>
</div>
</div>
<!-- require cdn assets js -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
<!-- built files will be auto injected -->
<!-- jessibuca -->
<script type="text/javascript" src="/js/jessibucaPro/jessibuca-pro-demo.js"></script>
<!-- jessibuca -->
</body>
</html>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
"use strict";var Module={};var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",data=>onmessage({data:data}));var fs=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:f=>(0,eval)(fs.readFileSync(f,"utf8")+"//# sourceURL="+f),postMessage:msg=>parentPort.postMessage(msg),performance:global.performance||{now:Date.now}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");if(ENVIRONMENT_IS_NODE){fs.writeSync(2,text+"\n");return}console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance)};self.onunhandledrejection=e=>{throw e.reason||e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./decoder-pro-mt-worker.js")).then(exports=>exports.default(Module))}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,0,0,1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){Module["__embind_initialize_bindings"]();initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err(`worker.js received unknown command ${e.data.cmd}`);err(e.data)}}catch(ex){if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}}self.onmessage=handleMessage;
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
"use strict";var Module={};var ENVIRONMENT_IS_NODE=typeof process=="object"&&typeof process.versions=="object"&&typeof process.versions.node=="string";if(ENVIRONMENT_IS_NODE){var nodeWorkerThreads=require("worker_threads");var parentPort=nodeWorkerThreads.parentPort;parentPort.on("message",data=>onmessage({data:data}));var fs=require("fs");Object.assign(global,{self:global,require:require,Module:Module,location:{href:__filename},Worker:nodeWorkerThreads.Worker,importScripts:f=>(0,eval)(fs.readFileSync(f,"utf8")+"//# sourceURL="+f),postMessage:msg=>parentPort.postMessage(msg),performance:global.performance||{now:Date.now}})}var initializedJS=false;function threadPrintErr(){var text=Array.prototype.slice.call(arguments).join(" ");if(ENVIRONMENT_IS_NODE){fs.writeSync(2,text+"\n");return}console.error(text)}function threadAlert(){var text=Array.prototype.slice.call(arguments).join(" ");postMessage({cmd:"alert",text:text,threadId:Module["_pthread_self"]()})}var err=threadPrintErr;self.alert=threadAlert;Module["instantiateWasm"]=(info,receiveInstance)=>{var module=Module["wasmModule"];Module["wasmModule"]=null;var instance=new WebAssembly.Instance(module,info);return receiveInstance(instance)};self.onunhandledrejection=e=>{throw e.reason||e};function handleMessage(e){try{if(e.data.cmd==="load"){let messageQueue=[];self.onmessage=e=>messageQueue.push(e);self.startWorker=instance=>{Module=instance;postMessage({"cmd":"loaded"});for(let msg of messageQueue){handleMessage(msg)}self.onmessage=handleMessage};Module["wasmModule"]=e.data.wasmModule;for(const handler of e.data.handlers){Module[handler]=(...args)=>{postMessage({cmd:"callHandler",handler:handler,args:args})}}Module["wasmMemory"]=e.data.wasmMemory;Module["buffer"]=Module["wasmMemory"].buffer;Module["ENVIRONMENT_IS_PTHREAD"]=true;(e.data.urlOrBlob?import(e.data.urlOrBlob):import("./decoder-pro-simd-mt-worker.js")).then(exports=>exports.default(Module))}else if(e.data.cmd==="run"){Module["__emscripten_thread_init"](e.data.pthread_ptr,0,0,1);Module["__emscripten_thread_mailbox_await"](e.data.pthread_ptr);Module["establishStackSpace"]();Module["PThread"].receiveObjectTransfer(e.data);Module["PThread"].threadInitTLS();if(!initializedJS){Module["__embind_initialize_bindings"]();initializedJS=true}try{Module["invokeEntryPoint"](e.data.start_routine,e.data.arg)}catch(ex){if(ex!="unwind"){throw ex}}}else if(e.data.cmd==="cancel"){if(Module["_pthread_self"]()){Module["__emscripten_thread_exit"](-1)}}else if(e.data.target==="setimmediate"){}else if(e.data.cmd==="checkMailbox"){if(initializedJS){Module["checkMailbox"]()}}else if(e.data.cmd){err(`worker.js received unknown command ${e.data.cmd}`);err(e.data)}}catch(ex){if(Module["__emscripten_thread_crashed"]){Module["__emscripten_thread_crashed"]()}throw ex}}self.onmessage=handleMessage;
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+34
View File
@@ -0,0 +1,34 @@
<svg width="120" height="32" viewBox="0 0 120 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- 主要图标部分 -->
<g id="logo-icon">
<!-- 中心核心 -->
<circle cx="16" cy="16" r="3" fill="#3370ff"/>
<!-- 连接节点 -->
<circle cx="6" cy="8" r="2" fill="#3370ff" opacity="0.8"/>
<circle cx="26" cy="8" r="2" fill="#3370ff" opacity="0.8"/>
<circle cx="6" cy="24" r="2" fill="#3370ff" opacity="0.8"/>
<circle cx="26" cy="24" r="2" fill="#3370ff" opacity="0.8"/>
<!-- 连接线 -->
<line x1="8" y1="9" x2="14" y2="14" stroke="#3370ff" stroke-width="1.5" opacity="0.6"/>
<line x1="24" y1="9" x2="18" y2="14" stroke="#3370ff" stroke-width="1.5" opacity="0.6"/>
<line x1="8" y1="23" x2="14" y2="18" stroke="#3370ff" stroke-width="1.5" opacity="0.6"/>
<line x1="24" y1="23" x2="18" y2="18" stroke="#3370ff" stroke-width="1.5" opacity="0.6"/>
<!-- 数据流动效果 -->
<circle cx="10" cy="11" r="1" fill="#00d4aa" opacity="0.9">
<animate attributeName="opacity" values="0.3;1;0.3" dur="2s" repeatCount="indefinite"/>
</circle>
<circle cx="22" cy="11" r="1" fill="#00d4aa" opacity="0.9">
<animate attributeName="opacity" values="1;0.3;1" dur="2s" repeatCount="indefinite"/>
</circle>
</g>
<!-- 文字部分 -->
<g id="logo-text">
<text x="40" y="20" font-family="PingFang SC, Microsoft YaHei, sans-serif" font-size="13"
font-weight="600" fill="#1d2129">IoT Platform
</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+32
View File
@@ -0,0 +1,32 @@
<template>
<a-config-provider :locale="locale">
<div id="app">
<router-view/>
<Resizabler ref="resizabler"/>
</div>
</a-config-provider>
</template>
<script>
import {domTitle, setDocumentTitle} from '@/utils/domUtil'
import {i18nRender} from '@/locales'
import Resizabler from '@/views/dashboard/components/Resizabler.vue'
export default {
components: {Resizabler},
computed: {
locale() {
// 只是为了切换语言时,更新标题
const {title} = this.$route.meta
title && (setDocumentTitle(`${i18nRender(title)} - ${domTitle}`))
return this.$i18n.getLocaleMessage(this.$store.getters.lang).antLocale
}
},
mounted() {
this.$bus.$on('resizeabler', (queryName) => {
this.$refs.resizabler.open(queryName)
})
}
}
</script>
@@ -0,0 +1,79 @@
import request from '@/utils/request'
// 查询用户应用信息列表
export function listApplication(query) {
return request({
url: '/admin/v1/apply/list',
method: 'get',
params: query
})
}
// 查询用户应用信息详细
export function getApplication(uuid) {
return request({
url: '/admin/v1/apply/' + uuid,
method: 'get'
})
}
// 新增用户应用信息
export function addApplication(data) {
return request({
url: '/admin/v1/apply',
method: 'post',
data: data
})
}
// 修改用户应用信息
export function updateApplication(data) {
return request({
url: '/admin/v1/apply',
method: 'put',
data: data
})
}
// 修改用户应用信息
export function enable(data) {
return request({
url: '/admin/v1/apply/enable',
method: 'post',
data: data
})
}
// 删除用户应用信息
export function delApplication(uuid) {
return request({
url: '/admin/v1/apply/' + uuid,
method: 'delete'
})
}
// 重置密钥
export function resetSecret(data) {
return request({
url: '/admin/v1/apply/reset',
method: 'put',
data: data
})
}
// 地址测试
export function checkUrl(query) {
return request({
url: '/admin/v1/apply/check',
method: 'get',
params: query
})
}
// 启用/停用推送配置
export function enableOrDisablePush(appUniqueId, pushType, enable) {
return request({
url: `/admin/v1/apply/${appUniqueId}/${pushType}/${enable}`,
method: 'post'
})
}
@@ -0,0 +1,125 @@
import request from '@/utils/request'
// 获取仪表盘概览数据
export function getDashboardOverview() {
return request({
url: '/dashboard/overview',
method: 'get'
})
}
// 获取设备消息分类图表数据
export function getDashboardMessageClassification(timeRange = 'today') {
return request({
url: '/api/dashboard/message-classification',
method: 'get',
params: {timeRange}
})
}
// 获取系统性能监控数据
export function getDashboardSystemMonitor(refreshInterval = 5) {
return request({
url: '/api/dashboard/system-monitor',
method: 'get',
params: {refreshInterval}
})
}
// 获取实时数据(兼容旧接口)
export function getDashboardRealtime() {
return request({
url: '/dashboard/realtime',
method: 'get'
})
}
// 获取告警数据(兼容旧接口)
export function getDashboardAlerts(params) {
return request({
url: '/dashboard/alerts',
method: 'get',
params
})
}
// 获取用户活动数据(兼容旧接口)
export function getDashboardUserActivity(params) {
return request({
url: '/dashboard/user-activity',
method: 'get',
params
})
}
// 获取图表数据(兼容旧接口)
export function getDashboardChartData(params) {
return request({
url: '/dashboard/chart-data',
method: 'get',
params
})
}
// 获取设备统计(兼容旧接口)
export function getDashboardDeviceStats(params) {
return request({
url: '/dashboard/device-stats',
method: 'get',
params
})
}
// 获取消息统计(兼容旧接口)
export function getDashboardMessageStats(params) {
return request({
url: '/dashboard/message-stats',
method: 'get',
params
})
}
// 获取渠道统计(兼容旧接口)
export function getDashboardChannelStats(params) {
return request({
url: '/dashboard/channel-stats',
method: 'get',
params
})
}
// 获取小时统计(兼容旧接口)
export function getDashboardHourlyStats(params) {
return request({
url: '/dashboard/hourly-stats',
method: 'get',
params
})
}
// 获取应用统计(兼容旧接口)
export function getDashboardAppStats(params) {
return request({
url: '/dashboard/app-stats',
method: 'get',
params
})
}
// 获取产品统计(兼容旧接口)
export function getDashboardProductStats(params) {
return request({
url: '/dashboard/product-stats',
method: 'get',
params
})
}
// 获取规则统计(兼容旧接口)
export function getDashboardRuleStats(params) {
return request({
url: '/dashboard/rule-stats',
method: 'get',
params
})
}
@@ -0,0 +1,14 @@
import request from '@/utils/request'
/**
* 国标应用列表
* @param {*} params
* @returns
*/
export function getGBAppList(params) {
return request({
url: '/admin/wvpThirdApplication/page',
method: 'get',
params
})
}
+310
View File
@@ -0,0 +1,310 @@
import request from '@/utils/request'
/**
* 查询摄像头列表
* @param {*} query
* @returns
*/
export function getCameraList(query) {
return request({
url: '/admin/wvpDevice/page',
method: 'get',
params: query
})
}
/**
* 添加摄像头
* @param {*} data
* @returns
*/
export function addDevice(data) {
return request({
url: '/admin/wvpDevice',
method: 'post',
data
})
}
/**
* 删除摄像头
* @param {*} ids
* @returns
*/
export function removeDevice(ids) {
return request({
url: `/admin/wvpDevice/${ids}`,
method: 'delete'
})
}
/**
* 修改摄像头
* @param {*} data
* @returns
*/
export function modifyDevice(data) {
return request({
url: `/admin/wvpDevice`,
method: 'put',
data
})
}
/**
* 查询设备详情
* @param {*} id
* @returns
*/
export function deviceDetail(id) {
return request({
url: `/admin/wvpDevice/${id}`,
method: 'get'
})
}
/**
* 查询流媒体节点
*/
export function getMediaIds() {
return request({
url: `/admin/wvpDevice/media`,
method: 'get'
})
}
/**
* 获取设备通道列表
* @param {*} deviceId
* @returns
*/
export function getChannelList(deviceId) {
return request({
url: `/admin/wvpDevice/channel/${deviceId}`,
method: 'get',
params: {
deviceId
}
})
}
/**
* 通道修改
* @param {*} data
* @returns
*/
export function modifyChannel(data) {
return request({
url: `/admin/wvpDevice/channel`,
method: 'put',
data
})
}
/**
* 摄像头取流
* @param {*} deviceId
* @param {*} channelId
* @returns
*/
export function getPlayUrl(deviceId, channelId) {
return request({
url: `/admin/wvpDevice/play/${deviceId}/${channelId}`,
method: 'get'
})
}
/**
* 停止取流
* @param {*} deviceId
* @param {*} channelId
* @returns
*/
export function stopPlayLive(deviceId, channelId) {
return request({
url: `/admin/wvpDevice/stop/${deviceId}/${channelId}`,
method: 'get'
})
}
/**
* 批量导入
* @param {*} productKey
* @returns
*/
export function handleBatchImport(productKey, data) {
return request({
url: `/admin/wvpDevice/import`,
method: 'post',
params: {
productKey
},
data
})
}
/**
* 摄像头获取本地录像列表
* @param {*} deviceId
* @param {*} channelId
* @param {*} startTime
* @param {*} endTime
* @returns
*/
export function getDeviceVideoTapeList(deviceId, channelId, startTime,
endTime) {
return request({
url: `/admin/wvpDevice/local/record/list/${deviceId}/${channelId}`,
method: 'get',
params: {
startTime,
endTime
}
})
}
/**
* 获取本地录像段
* @param {*} deviceId
* @param {*} channelId
* @param {*} startTime
* @param {*} endTime
* @returns
*/
export function getDeviceVideoTapeCtx(deviceId, channelId, startTime, endTime) {
return request({
url: `/admin/wvpDevice/local/record/play/${deviceId}/${channelId}`,
method: 'get',
params: {
startTime,
endTime
}
})
}
/**
* 倍速播放本地录像
* @param {*} deviceId
* @param {*} channelId
* @param {*} stream
* @param {*} speed
* @returns
*/
export function getScaleDeviceVideoTapeCtx(deviceId, channelId, stream, speed) {
return request({
url: `/admin/wvpDevice/local/record/speed/${deviceId}/${channelId}/${stream}/${speed}`,
method: 'get'
})
}
/**
* 停止播放本地录像
* @param {*} deviceId
* @param {*} channelId
* @param {*} stream
* @returns
*/
export function stopDeviceVideoTape(deviceId, channelId, stream) {
return request({
url: `/admin/wvpDevice/local/record/stop/${deviceId}/${channelId}/${stream}`,
method: 'get'
})
}
/**
* 暂停播放本地录像
* @param {*} deviceId
* @param {*} channelId
* @param {*} stream
* @returns
*/
export function pauseDeviceVideoTape(deviceId, channelId, stream) {
return request({
url: `/admin/wvpDevice/local/record/pause/${deviceId}/${channelId}/${stream}`,
method: 'get'
})
}
/**
* 恢复播放本地录像
* @param {*} deviceId
* @param {*} channelId
* @param {*} stream
* @returns
*/
export function resumeDeviceVideoTape(deviceId, channelId, stream) {
return request({
url: `/admin/wvpDevice/local/record/resume/${deviceId}/${channelId}/${stream}`,
method: 'get'
})
}
/**
* 开始下载本地录像
* @param {*} deviceId
* @param {*} channelId
* @param {*} startTime
* @param {*} endTime
* @param {*} downloadSpeed 倍速
* @returns
*/
export function downloadDeviceVideoTape(deviceId, channelId, startTime, endTime,
downloadSpeed = 128) {
return request({
url: `/admin/wvpDevice/local/record/download/start/${deviceId}/${channelId}`,
method: 'get',
params: {
startTime,
endTime,
downloadSpeed
}
})
}
/**
* 获取设备录像下载进度
* @param {*} deviceId
* @param {*} channelId
* @param {*} stream
* @returns
*/
export function getDownloadDeviceProgress(deviceId, channelId, stream) {
return request({
url: `/admin/wvpDevice/local/record/download/progress/${deviceId}/${channelId}/${stream}`,
method: 'get'
})
}
/**
* 摄像头获取云端录像列表
* @param {*} deviceId
* @param {*} channelId
* @returns
*/
export function getCloudVideoTapeList(deviceId, channelId, startTime, endTime) {
return request({
url: `/admin/wvpDevice/cloud/record/list/${deviceId}/${channelId}`,
method: 'get',
params: {
startTime,
endTime
}
})
}
/**
* 获取云端录像片段
* @param {*} deviceId
* @param {*} channelId
* @param {*} recordId
* @returns
*/
export function getCloudVideoTapeCtx(deviceId, channelId, recordId) {
return request({
url: `/admin/wvpDevice/cloud/record/play/${deviceId}/${channelId}`,
method: 'get',
params: {
recordId
}
})
}

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