前端-胡立永 3 months ago
parent
commit
cdfcdea133
43 changed files with 3306 additions and 12 deletions
  1. +83
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/TTS接口使用说明.md
  2. +6
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml
  3. +59
    -10
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiTTSController.java
  4. +32
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiTTService.java
  5. +0
    -1
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiLoginService.java
  6. +161
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java
  7. +182
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/controller/AppletTtsPlayLogController.java
  8. +87
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/entity/AppletTtsPlayLog.java
  9. +17
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/mapper/AppletTtsPlayLogMapper.java
  10. +5
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/mapper/xml/AppletTtsPlayLogMapper.xml
  11. +14
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/service/IAppletTtsPlayLogService.java
  12. +19
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/service/impl/AppletTtsPlayLogServiceImpl.java
  13. +120
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp/AppletTtsPlayLogForm.vue
  14. +44
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp/AppletTtsPlayLogList.vue
  15. +47
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp3/AppletTtsPlayLogData.ts
  16. +298
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp3/AppletTtsPlayLogForm.vue
  17. +148
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp3/AppletTtsPlayLogList.vue
  18. +64
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/AppletTtsPlayLog.api.ts
  19. +122
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/AppletTtsPlayLog.data.ts
  20. +206
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/AppletTtsPlayLogList.vue
  21. +26
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/V20250912_1__menu_insert_AppletTtsPlayLog.sql
  22. +70
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/components/AppletTtsPlayLogForm.vue
  23. +99
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/components/AppletTtsPlayLogModal.vue
  24. +182
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/controller/AppletTtsTimbreController.java
  25. +71
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/entity/AppletTtsTimbre.java
  26. +17
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/mapper/AppletTtsTimbreMapper.java
  27. +5
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/mapper/xml/AppletTtsTimbreMapper.xml
  28. +14
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/service/IAppletTtsTimbreService.java
  29. +19
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/service/impl/AppletTtsTimbreServiceImpl.java
  30. +95
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp/AppletTtsTimbreForm.vue
  31. +44
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp/AppletTtsTimbreList.vue
  32. +19
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp3/AppletTtsTimbreData.ts
  33. +236
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp3/AppletTtsTimbreForm.vue
  34. +148
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp3/AppletTtsTimbreList.vue
  35. +64
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/AppletTtsTimbre.api.ts
  36. +67
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/AppletTtsTimbre.data.ts
  37. +206
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/AppletTtsTimbreList.vue
  38. +26
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/V20250912_1__menu_insert_AppletTtsTimbre.sql
  39. +70
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/components/AppletTtsTimbreForm.vue
  40. +99
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/components/AppletTtsTimbreModal.vue
  41. +6
    -0
      jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/resources/application.yml
  42. +1
    -0
      jeecg-boot/jeecg-module-system/jeecg-system-start/pom.xml
  43. +8
    -1
      jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml

+ 83
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/TTS接口使用说明.md View File

@ -0,0 +1,83 @@
# 腾讯云TTS文字转语音接口使用说明
## 接口地址
```
POST /appletApi/tts/textToVoice
```
## 请求参数
| 参数名称 | 类型 | 必填 | 描述 |
|---------|------|------|------|
| text | String | 是 | 要转换的文本内容,中文最大支持150个汉字,英文最大支持500个字母 |
| speed | Float | 否 | 语速,范围:[-2,6],默认为0。-2代表0.6倍,-1代表0.8倍,0代表1.0倍,1代表1.2倍,2代表1.5倍,6代表2.5倍 |
| voiceType | Integer | 否 | 音色ID,默认为0。不同音色价格有差异,完整音色列表请参见腾讯云官方文档 |
| volume | Float | 否 | 音量大小,范围[-10,10],默认为0,代表正常音量 |
| codec | String | 否 | 返回音频格式,可取值:wav(默认),mp3,pcm |
## 返回结果
成功时返回二进制音频数据,Content-Type根据codec参数设置:
- wav格式:audio/wav
- mp3格式:audio/mpeg
- pcm格式:audio/pcm
## 使用示例
### 基本调用
```javascript
// 前端调用示例
fetch('/appletApi/tts/textToVoice', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'text=你好,欢迎使用语音合成服务'
})
.then(response => response.blob())
.then(blob => {
const audio = new Audio(URL.createObjectURL(blob));
audio.play();
});
```
### 带参数调用
```javascript
// 设置语速和音色
fetch('/appletApi/tts/textToVoice', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'text=Hello World&speed=1.2&voiceType=1&volume=2&codec=mp3'
})
.then(response => response.blob())
.then(blob => {
const audio = new Audio(URL.createObjectURL(blob));
audio.play();
});
```
## 配置说明
`application.yml` 中配置腾讯云密钥:
```yaml
tencent:
secretId: 你的腾讯云SecretId
secretKey: 你的腾讯云SecretKey
```
## 注意事项
1. 需要在腾讯云控制台开通语音合成服务
2. 确保配置的密钥有语音合成的权限
3. 文本长度限制:中文最大150个汉字,英文最大500个字母
4. 返回的音频数据可以直接在前端播放
5. 不同音色和采样率可能影响合成效果和费用
## 错误处理
- 如果返回204 No Content,表示合成失败或返回数据为空
- 如果返回500 Internal Server Error,表示服务器内部错误,请检查日志
- 请确保网络连接正常,能够访问腾讯云API服务

+ 6
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/pom.xml View File

@ -39,6 +39,12 @@
<artifactId>module-pay</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-tts</artifactId>
<version>3.1.1305</version>
</dependency>
</dependencies>
</project>

+ 59
- 10
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiTTSController.java View File

@ -2,13 +2,19 @@ package org.jeecg.modules.applet.controller;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.jeecg.modules.applet.service.AppletApiTTService;
import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import java.util.List;
@Slf4j
@Tag(name = "文字转语音", description = "文字转语音")
@ -16,16 +22,59 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/appletApi/tts")
public class AppletApiTTSController {
@Autowired
private AppletApiTTService appletApiTTService;
@Operation(summary = "查询音色列表【待开发】", description = "查询音色列表")
@GetMapping(value = "/list")
public Result<?> list() {
return Result.OK();
public Result<List<AppletTtsTimbre>> list() {
return Result.OK(appletApiTTService.list());
}
@Operation(summary = "文字转语音【待开发】", description = "文字转语音")
@PostMapping(value = "/play")
public Result<?> play() {
return Result.OK();
@Operation(summary = "文字转语音", description = "文字转语音")
@PostMapping(value = "/textToVoice")
public ResponseEntity<byte[]> textToVoice(
@Parameter(description = "要转换的文本内容", required = true) String text,
@Parameter(description = "语速,范围:[-2,6],默认为0-2代表0.6倍\n" +
"-1代表0.8倍\n" +
"0代表1.0倍(默认)\n" +
"1代表1.2倍\n" +
"2代表1.5倍\n" +
"6代表2.5倍") Float speed,
@Parameter(description = "音色ID,默认为0") Integer voiceType,
@Parameter(description = "音量大小,范围[-10,10],默认为0") Float volume,
@Parameter(description = "返回音频格式,可取值:wav(默认),mp3,pcm") String codec,
@Parameter(description = "用户ID,用于记录日志") String userId) {
try {
byte[] audioData = appletApiTTService.textToVoice(text, speed, voiceType, volume, codec, userId);
if (audioData == null || audioData.length == 0) {
return ResponseEntity.noContent().build();
}
// 根据codec设置对应的Content-Type
MediaType mediaType = MediaType.APPLICATION_OCTET_STREAM;
if ("wav".equalsIgnoreCase(codec)) {
mediaType = MediaType.parseMediaType("audio/wav");
} else if ("mp3".equalsIgnoreCase(codec)) {
mediaType = MediaType.parseMediaType("audio/mpeg");
} else if ("pcm".equalsIgnoreCase(codec)) {
mediaType = MediaType.parseMediaType("audio/pcm");
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(mediaType);
headers.setContentLength(audioData.length);
return ResponseEntity.ok()
.headers(headers)
.body(audioData);
} catch (Exception e) {
log.error("文字转语音失败: {}", e.getMessage(), e);
return ResponseEntity.internalServerError().build();
}
}
}

+ 32
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiTTService.java View File

@ -0,0 +1,32 @@
package org.jeecg.modules.applet.service;
import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre;
import java.util.List;
public interface AppletApiTTService {
/**
* 文字转语音
* @param text 要转换的文本内容
* @param speed 语速范围[-26]默认为0
* @param voiceType 音色ID默认为0
* @param volume 音量大小范围[-1010]默认为0
* @param codec 返回音频格式可取值wav默认mp3pcm
* @return 音频二进制数据
*/
byte[] textToVoice(String text, Float speed, Integer voiceType, Float volume, String codec);
/**
* 文字转语音带用户ID记录日志
* @param text 要转换的文本内容
* @param speed 语速范围[-26]默认为0
* @param voiceType 音色ID默认为0
* @param volume 音量大小范围[-1010]默认为0
* @param codec 返回音频格式可取值wav默认mp3pcm
* @param userId 用户ID用于记录日志
* @return 音频二进制数据
*/
byte[] textToVoice(String text, Float speed, Integer voiceType, Float volume, String codec, String userId);
List<AppletTtsTimbre> list();
}

+ 0
- 1
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiLoginService.java View File

@ -251,7 +251,6 @@ public class AppletApiLoginService {
public Result<AppletUser> getUserInfo() {
try {
// 获取当前登录用户
AppletUser user = AppletUserUtil.getCurrentAppletUser();
if (user == null) {


+ 161
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiTTServiceImpl.java View File

@ -0,0 +1,161 @@
package org.jeecg.modules.applet.service.impl;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.tts.v20190823.TtsClient;
import com.tencentcloudapi.tts.v20190823.models.TextToVoiceRequest;
import com.tencentcloudapi.tts.v20190823.models.TextToVoiceResponse;
import lombok.extern.log4j.Log4j2;
import org.jeecg.modules.applet.service.AppletApiTTService;
import org.jeecg.modules.demo.appletTtsPlayLog.service.IAppletTtsPlayLogService;
import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre;
import org.jeecg.modules.demo.appletTtsTimbre.service.IAppletTtsTimbreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
@Log4j2
@Service
public class AppletApiTTServiceImpl implements AppletApiTTService {
@Value("${tencent.secretId}")
private String secretId;
@Value("${tencent.secretKey}")
private String secretKey;
@Autowired
private IAppletTtsTimbreService appletTtsTimbreService;
@Autowired
private IAppletTtsPlayLogService appletTtsPlayLogService;
@Override
public byte[] textToVoice(String text, Float speed, Integer voiceType, Float volume, String codec) {
return textToVoice(text, speed, voiceType, volume, codec, null);
}
public byte[] textToVoice(String text, Float speed, Integer voiceType, Float volume, String codec, String userId) {
long startTime = System.currentTimeMillis();
try {
// 创建认证对象
Credential cred = new Credential(secretId, secretKey);
// 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
httpProfile.setEndpoint("tts.tencentcloudapi.com");
// 实例化一个client选项可选的没有特殊需求可以跳过
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
// 实例化要请求产品的client对象
TtsClient client = new TtsClient(cred, "", clientProfile);
// 实例化一个请求对象
TextToVoiceRequest req = new TextToVoiceRequest();
// 设置必填参数
req.setText(text);
req.setSessionId(UUID.randomUUID().toString());
// 设置可选参数
if (speed != null) {
req.setSpeed(speed);
}
if (voiceType != null) {
req.setVoiceType(voiceType.longValue());
}
if (volume != null) {
req.setVolume(volume);
}
if (codec != null && !codec.isEmpty()) {
req.setCodec(codec);
} else {
req.setCodec("wav"); // 默认wav格式
}
// 设置其他默认参数
req.setModelType(1L); // 默认模型
req.setPrimaryLanguage(2L); // 中文
req.setSampleRate(16000L); // 16k采样率
// 返回的resp是一个TextToVoiceResponse的实例与请求对象对应
TextToVoiceResponse resp = client.TextToVoice(req);
// 计算耗时
long endTime = System.currentTimeMillis();
double elapsedTime = (endTime - startTime) / 1000.0;
// 获取base64编码的音频数据并解码为二进制
String audioBase64 = resp.getAudio();
byte[] audioData = null;
if (audioBase64 != null && !audioBase64.isEmpty()) {
audioData = java.util.Base64.getDecoder().decode(audioBase64);
// 记录成功的TTS调用日志
savePlayLog(userId, text, voiceType, volume != null ? volume.doubleValue() : null,
speed != null ? speed.doubleValue() : null, elapsedTime, true);
log.info("TTS调用成功,文本长度: {}, 耗时: {}秒", text != null ? text.length() : 0, elapsedTime);
return audioData;
} else {
// 记录失败的TTS调用日志
savePlayLog(userId, text, voiceType, volume != null ? volume.doubleValue() : null,
speed != null ? speed.doubleValue() : null, elapsedTime, false);
log.warn("TTS返回的音频数据为空");
return null;
}
} catch (Exception e) {
// 计算耗时
long endTime = System.currentTimeMillis();
double elapsedTime = (endTime - startTime) / 1000.0;
// 记录失败的TTS调用日志
savePlayLog(userId, text, voiceType, volume != null ? volume.doubleValue() : null,
speed != null ? speed.doubleValue() : null, elapsedTime, false);
log.error("调用腾讯云TTS接口失败: {}", e.getMessage(), e);
return null;
}
}
/**
* 保存TTS播放日志
*/
private void savePlayLog(String userId, String text, Integer voiceType, Double volume,
Double speed, Double elapsedTime, boolean success) {
try {
org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog playLog =
new org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog();
playLog.setUserId(userId);
playLog.setText(text);
playLog.setVoicetype(voiceType);
playLog.setVolume(volume);
playLog.setSpeed(speed);
playLog.setElapsedTime(elapsedTime);
playLog.setSuccess(success ? "Y" : "N");
playLog.setCreateTime(new java.util.Date());
appletTtsPlayLogService.save(playLog);
log.debug("TTS播放日志保存成功,用户: {}, 结果: {}", userId, success ? "成功" : "失败");
} catch (Exception e) {
log.error("保存TTS播放日志失败: {}", e.getMessage(), e);
}
}
@Override
public List<AppletTtsTimbre> list() {
return appletTtsTimbreService.list();
}
}

+ 182
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/controller/AppletTtsPlayLogController.java View File

@ -0,0 +1,182 @@
package org.jeecg.modules.demo.appletTtsPlayLog.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog;
import org.jeecg.modules.demo.appletTtsPlayLog.service.IAppletTtsPlayLogService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 小程序语音朗读记录
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
@Tag(name="小程序语音朗读记录")
@RestController
@RequestMapping("/appletTtsPlayLog/appletTtsPlayLog")
@Slf4j
public class AppletTtsPlayLogController extends JeecgController<AppletTtsPlayLog, IAppletTtsPlayLogService> {
@Autowired
private IAppletTtsPlayLogService appletTtsPlayLogService;
/**
* 分页列表查询
*
* @param appletTtsPlayLog
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "小程序语音朗读记录-分页列表查询")
@Operation(summary="小程序语音朗读记录-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AppletTtsPlayLog>> queryPageList(AppletTtsPlayLog appletTtsPlayLog,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AppletTtsPlayLog> queryWrapper = QueryGenerator.initQueryWrapper(appletTtsPlayLog, req.getParameterMap());
Page<AppletTtsPlayLog> page = new Page<AppletTtsPlayLog>(pageNo, pageSize);
IPage<AppletTtsPlayLog> pageList = appletTtsPlayLogService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param appletTtsPlayLog
* @return
*/
@AutoLog(value = "小程序语音朗读记录-添加")
@Operation(summary="小程序语音朗读记录-添加")
@RequiresPermissions("appletTtsPlayLog:applet_tts_play_log:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AppletTtsPlayLog appletTtsPlayLog) {
appletTtsPlayLogService.save(appletTtsPlayLog);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param appletTtsPlayLog
* @return
*/
@AutoLog(value = "小程序语音朗读记录-编辑")
@Operation(summary="小程序语音朗读记录-编辑")
@RequiresPermissions("appletTtsPlayLog:applet_tts_play_log:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AppletTtsPlayLog appletTtsPlayLog) {
appletTtsPlayLogService.updateById(appletTtsPlayLog);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "小程序语音朗读记录-通过id删除")
@Operation(summary="小程序语音朗读记录-通过id删除")
@RequiresPermissions("appletTtsPlayLog:applet_tts_play_log:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
appletTtsPlayLogService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "小程序语音朗读记录-批量删除")
@Operation(summary="小程序语音朗读记录-批量删除")
@RequiresPermissions("appletTtsPlayLog:applet_tts_play_log:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.appletTtsPlayLogService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "小程序语音朗读记录-通过id查询")
@Operation(summary="小程序语音朗读记录-通过id查询")
@GetMapping(value = "/queryById")
public Result<AppletTtsPlayLog> queryById(@RequestParam(name="id",required=true) String id) {
AppletTtsPlayLog appletTtsPlayLog = appletTtsPlayLogService.getById(id);
if(appletTtsPlayLog==null) {
return Result.error("未找到对应数据");
}
return Result.OK(appletTtsPlayLog);
}
/**
* 导出excel
*
* @param request
* @param appletTtsPlayLog
*/
@RequiresPermissions("appletTtsPlayLog:applet_tts_play_log:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AppletTtsPlayLog appletTtsPlayLog) {
return super.exportXls(request, appletTtsPlayLog, AppletTtsPlayLog.class, "小程序语音朗读记录");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("appletTtsPlayLog:applet_tts_play_log:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AppletTtsPlayLog.class);
}
}

+ 87
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/entity/AppletTtsPlayLog.java View File

@ -0,0 +1,87 @@
package org.jeecg.modules.demo.appletTtsPlayLog.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 小程序语音朗读记录
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
@Data
@TableName("applet_tts_play_log")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="小程序语音朗读记录")
public class AppletTtsPlayLog implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
/**所属部门*/
@Schema(description = "所属部门")
private java.lang.String sysOrgCode;
/**用户*/
@Excel(name = "用户", width = 15)
@Schema(description = "用户")
private java.lang.String userId;
/**文本*/
@Excel(name = "文本", width = 15)
@Schema(description = "文本")
private java.lang.String text;
/**音色*/
@Excel(name = "音色", width = 15)
@Schema(description = "音色")
private java.lang.Integer voicetype;
/**音量*/
@Excel(name = "音量", width = 15)
@Schema(description = "音量")
private java.lang.Double volume;
/**语速*/
@Excel(name = "语速", width = 15)
@Schema(description = "语速")
private java.lang.Double speed;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**耗时*/
@Excel(name = "耗时", width = 15)
@Schema(description = "耗时")
private java.lang.Double elapsedTime;
/**是否成功*/
@Excel(name = "是否成功", width = 15,replace = {"是_Y","否_N"} )
@Schema(description = "是否成功")
private java.lang.String success;
}

+ 17
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/mapper/AppletTtsPlayLogMapper.java View File

@ -0,0 +1,17 @@
package org.jeecg.modules.demo.appletTtsPlayLog.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @Description: 小程序语音朗读记录
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
public interface AppletTtsPlayLogMapper extends BaseMapper<AppletTtsPlayLog> {
}

+ 5
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/mapper/xml/AppletTtsPlayLogMapper.xml View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.demo.appletTtsPlayLog.mapper.AppletTtsPlayLogMapper">
</mapper>

+ 14
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/service/IAppletTtsPlayLogService.java View File

@ -0,0 +1,14 @@
package org.jeecg.modules.demo.appletTtsPlayLog.service;
import org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @Description: 小程序语音朗读记录
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
public interface IAppletTtsPlayLogService extends IService<AppletTtsPlayLog> {
}

+ 19
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/service/impl/AppletTtsPlayLogServiceImpl.java View File

@ -0,0 +1,19 @@
package org.jeecg.modules.demo.appletTtsPlayLog.service.impl;
import org.jeecg.modules.demo.appletTtsPlayLog.entity.AppletTtsPlayLog;
import org.jeecg.modules.demo.appletTtsPlayLog.mapper.AppletTtsPlayLogMapper;
import org.jeecg.modules.demo.appletTtsPlayLog.service.IAppletTtsPlayLogService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
/**
* @Description: 小程序语音朗读记录
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
@Service
public class AppletTtsPlayLogServiceImpl extends ServiceImpl<AppletTtsPlayLogMapper, AppletTtsPlayLog> implements IAppletTtsPlayLogService {
}

+ 120
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp/AppletTtsPlayLogForm.vue View File

@ -0,0 +1,120 @@
<template>
<view>
<!--标题和返回-->
<cu-custom :bgColor="NavBarColor" isBack :backRouterName="backRouteName">
<block slot="backText">返回</block>
<block slot="content">小程序语音朗读记录</block>
</cu-custom>
<!--表单区域-->
<view>
<form>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">用户</text></view>
<input placeholder="请输入用户" v-model="model.userId"/>
</view>
</view>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">文本</text></view>
<input placeholder="请输入文本" v-model="model.text"/>
</view>
</view>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">音色</text></view>
<input type="number" placeholder="请输入音色" v-model="model.voicetype"/>
</view>
</view>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">音量</text></view>
<input type="number" placeholder="请输入音量" v-model="model.volume"/>
</view>
</view>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">语速</text></view>
<input type="number" placeholder="请输入语速" v-model="model.speed"/>
</view>
</view>
<my-date label="创建日期:" v-model="model.createTime" placeholder="请输入创建日期"></my-date>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">耗时</text></view>
<input type="number" placeholder="请输入耗时" v-model="model.elapsedTime"/>
</view>
</view>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">是否成功</text></view>
<input placeholder="请输入是否成功" v-model="model.success"/>
</view>
</view>
<view class="padding">
<button class="cu-btn block bg-blue margin-tb-sm lg" @click="onSubmit">
<text v-if="loading" class="cuIcon-loading2 cuIconfont-spin"></text>提交
</button>
</view>
</form>
</view>
</view>
</template>
<script>
import myDate from '@/components/my-componets/my-date.vue'
export default {
name: "AppletTtsPlayLogForm",
components:{ myDate },
props:{
formData:{
type:Object,
default:()=>{},
required:false
}
},
data(){
return {
CustomBar: this.CustomBar,
NavBarColor: this.NavBarColor,
loading:false,
model: {},
backRouteName:'index',
url: {
queryById: "/appletTtsPlayLog/appletTtsPlayLog/queryById",
add: "/appletTtsPlayLog/appletTtsPlayLog/add",
edit: "/appletTtsPlayLog/appletTtsPlayLog/edit",
},
}
},
created(){
this.initFormData();
},
methods:{
initFormData(){
if(this.formData){
let dataId = this.formData.dataId;
this.$http.get(this.url.queryById,{params:{id:dataId}}).then((res)=>{
if(res.data.success){
console.log("表单数据",res);
this.model = res.data.result;
}
})
}
},
onSubmit() {
let myForm = {...this.model};
this.loading = true;
let url = myForm.id?this.url.edit:this.url.add;
this.$http.post(url,myForm).then(res=>{
console.log("res",res)
this.loading = false
this.$Router.push({name:this.backRouteName})
}).catch(()=>{
this.loading = false
});
}
}
}
</script>

+ 44
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp/AppletTtsPlayLogList.vue View File

@ -0,0 +1,44 @@
<template>
<view>
<!--标题和返回-->
<cu-custom :bgColor="NavBarColor" isBack>
<block slot="backText">返回</block>
<block slot="content">小程序语音朗读记录</block>
</cu-custom>
<!--滚动加载列表-->
<mescroll-body ref="mescrollRef" bottom="88" @init="mescrollInit" :up="upOption" :down="downOption" @down="downCallback" @up="upCallback">
<view class="cu-list menu">
<view class="cu-item" v-for="(item,index) in list" :key="index" @click="goHome">
<view class="flex" style="width:100%">
<text class="text-lg" style="color: #000;">
{{ item.createBy}}
</text>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
import Mixin from "@/common/mixin/Mixin.js";
export default {
name: '小程序语音朗读记录',
mixins: [MescrollMixin,Mixin],
data() {
return {
CustomBar:this.CustomBar,
NavBarColor:this.NavBarColor,
url: "/appletTtsPlayLog/appletTtsPlayLog/list",
};
},
methods: {
goHome(){
this.$Router.push({name: "index"})
}
}
}
</script>

+ 47
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp3/AppletTtsPlayLogData.ts View File

@ -0,0 +1,47 @@
import { render } from '@/common/renderUtils';
//列表数据
export const columns = [
{
title: '用户',
align:"center",
dataIndex: 'userId'
},
{
title: '文本',
align:"center",
dataIndex: 'text'
},
{
title: '音色',
align:"center",
dataIndex: 'voicetype'
},
{
title: '音量',
align:"center",
dataIndex: 'volume'
},
{
title: '语速',
align:"center",
dataIndex: 'speed'
},
{
title: '创建日期',
align:"center",
dataIndex: 'createTime'
},
{
title: '耗时',
align:"center",
dataIndex: 'elapsedTime'
},
{
title: '是否成功',
align:"center",
dataIndex: 'success',
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'Y'},{text:'否',value:'N'}])
},
},
];

+ 298
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp3/AppletTtsPlayLogForm.vue View File

@ -0,0 +1,298 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '小程序语音朗读记录',
},
}
</route>
<template>
<PageLayout :navTitle="navTitle" :backRouteName="backRouteName">
<scroll-view class="scrollArea" scroll-y>
<view class="form-container">
<wd-form ref="form" :model="myFormData">
<wd-cell-group border>
<view class="{ 'mt-14px': 0 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['userId']"
:label="get4Label('用户')"
name='userId'
prop='userId'
placeholder="请选择用户"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 1 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['text']"
:label="get4Label('文本')"
name='text'
prop='text'
placeholder="请选择文本"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 0 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['voicetype']"
:label="get4Label('音色')"
name='voicetype'
prop='voicetype'
placeholder="请选择音色"
inputMode="numeric"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 1 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['volume']"
:label="get4Label('音量')"
name='volume'
prop='volume'
placeholder="请选择音量"
inputMode="numeric"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 0 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['speed']"
:label="get4Label('语速')"
name='speed'
prop='speed'
placeholder="请选择语速"
inputMode="numeric"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 1 == 0 }">
</view>
<view class="{ 'mt-14px': 0 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['elapsedTime']"
:label="get4Label('耗时')"
name='elapsedTime'
prop='elapsedTime'
placeholder="请选择耗时"
inputMode="numeric"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 1 == 0 }">
<!-- 开关 -->
<wd-cell
:label="get4Label('是否成功')"
name='success'
title-width="100px"
center
>
<wd-switch
:label="get4Label('是否成功')"
name='success'
size="18px"
v-model="myFormData['success']"
active-value="Y"
inactive-value="N"
/>
</wd-cell>
</view>
</wd-cell-group>
</wd-form>
</view>
</scroll-view>
<view class="footer">
<wd-button :disabled="loading" block :loading="loading" @click="handleSubmit">提交</wd-button>
</view>
</PageLayout>
</template>
<script lang="ts" setup>
import { onLoad } from '@dcloudio/uni-app'
import { http } from '@/utils/http'
import { useToast } from 'wot-design-uni'
import { useRouter } from '@/plugin/uni-mini-router'
import { ref, onMounted, computed,reactive } from 'vue'
import OnlineImage from '@/components/online/view/online-image.vue'
import OnlineFile from '@/components/online/view/online-file.vue'
import OnlineFileCustom from '@/components/online/view/online-file-custom.vue'
import OnlineSelect from '@/components/online/view/online-select.vue'
import OnlineTime from '@/components/online/view/online-time.vue'
import OnlineDate from '@/components/online/view/online-date.vue'
import OnlineRadio from '@/components/online/view/online-radio.vue'
import OnlineCheckbox from '@/components/online/view/online-checkbox.vue'
import OnlineMulti from '@/components/online/view/online-multi.vue'
import OnlinePopupLinkRecord from '@/components/online/view/online-popup-link-record.vue'
import OnlinePca from '@/components/online/view/online-pca.vue'
import SelectDept from '@/components/SelectDept/SelectDept.vue'
import SelectUser from '@/components/SelectUser/SelectUser.vue'
import {duplicateCheck} from "@/service/api";
defineOptions({
name: 'AppletTtsPlayLogForm',
options: {
styleIsolation: 'shared',
},
})
const toast = useToast()
const router = useRouter()
const form = ref(null)
//
const myFormData = reactive({})
const loading = ref(false)
const navTitle = ref('新增')
const dataId = ref('')
const backRouteName = ref('AppletTtsPlayLogList')
// initForm
const initForm = (item) => {
console.log('initForm item', item)
if(item?.dataId){
dataId.value = item.dataId;
navTitle.value = item.dataId?'编辑':'新增';
initData();
}
}
//
const initData = () => {
http.get("/appletTtsPlayLog/appletTtsPlayLog/queryById",{id:dataId.value}).then((res) => {
if (res.success) {
let obj = res.result
Object.assign(myFormData, { ...obj })
}else{
toast.error(res?.message || '表单加载失败!')
}
})
}
const handleSuccess = () => {
uni.$emit('refreshList');
router.back()
}
/**
* 校验唯一
* @param values
* @returns {boolean}
*/
async function fieldCheck(values: any) {
const onlyField = [
];
for (const field of onlyField) {
if (values[field]) {
//
const res: any = await duplicateCheck({
tableName: 'applet_tts_play_log',
fieldName: field, // 使
fieldVal: values[field],
dataId: values.id,
});
if (!res.success) {
toast.warning(res.message);
return true; //
}
}
}
return false; //
}
//
const handleSubmit = async () => {
//
if (await fieldCheck(myFormData)) {
return
}
let url = dataId.value?'/appletTtsPlayLog/appletTtsPlayLog/edit':'/appletTtsPlayLog/appletTtsPlayLog/add';
form.value
.validate()
.then(({ valid, errors }) => {
if (valid) {
loading.value = true;
http.post(url,myFormData).then((res) => {
loading.value = false;
if (res.success) {
toast.success('保存成功');
handleSuccess()
}else{
toast.error(res?.message || '表单保存失败!')
}
})
}
})
.catch((error) => {
console.log(error, 'error')
loading.value = false;
})
}
//
const get4Label = computed(() => {
return (label) => {
return label && label.length > 4 ? label.substring(0, 4) : label;
}
})
//
const getFormSchema = computed(() => {
return (dictTable,dictCode,dictText) => {
return {
dictCode,
dictTable,
dictText
};
}
})
/**
* 获取日期控件的扩展类型
* @param picker
* @returns {string}
*/
const getDateExtendType = (picker: string) => {
let mapField = {
month: 'year-month',
year: 'year',
quarter: 'quarter',
week: 'week',
day: 'date',
}
return picker && mapField[picker]
? mapField[picker]
: 'date'
}
//pop
const setFieldsValue = (data) => {
Object.assign(myFormData, {...data })
}
// onLoad
onLoad((option) => {
initForm(option)
})
</script>
<style lang="scss" scoped>
.footer {
width: 100%;
padding: 10px 20px;
padding-bottom: calc(constant(safe-area-inset-bottom) + 10px);
padding-bottom: calc(env(safe-area-inset-bottom) + 10px);
}
:deep(.wd-cell__label) {
font-size: 14px;
color: #444;
}
:deep(.wd-cell__value) {
text-align: left;
}
</style>

+ 148
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/uniapp3/AppletTtsPlayLogList.vue View File

@ -0,0 +1,148 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationBarTitleText: '小程序语音朗读记录',
navigationStyle: 'custom',
},
}
</route>
<template>
<PageLayout navTitle="小程序语音朗读记录" backRouteName="index" routeMethod="pushTab">
<view class="wrap">
<z-paging
ref="paging"
:fixed="false"
v-model="dataList"
@query="queryList"
:default-page-size="15"
>
<template v-for="item in dataList" :key="item.id">
<wd-swipe-action>
<view class="list" @click="handleEdit(item)">
<template v-for="(cItem, cIndex) in columns" :key="cIndex">
<view v-if="cIndex < 3" class="box" :style="getBoxStyle">
<view class="field ellipsis">{{ cItem.title }}</view>
<view class="value cu-text-grey">{{ item[cItem.dataIndex] }}</view>
</view>
</template>
</view>
<template #right>
<view class="action">
<view class="button" @click="handleAction('del', item)">删除</view>
</view>
</template>
</wd-swipe-action>
</template>
</z-paging>
<view class="add u-iconfont u-icon-add" @click="handleAdd"></view>
</view>
</PageLayout>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { http } from '@/utils/http'
import usePageList from '@/hooks/usePageList'
import {columns} from './AppletTtsPlayLogData';
defineOptions({
name: 'AppletTtsPlayLogList',
options: {
styleIsolation: 'shared',
}
})
//
let { toast, router, paging, dataList, queryList } = usePageList('/appletTtsPlayLog/appletTtsPlayLog/list');
//
const getBoxStyle = computed(() => {
return { width: "calc(33% - 5px)" }
})
//
const handleAction = (val, item) => {
if (val == 'del') {
http.delete("/appletTtsPlayLog/appletTtsPlayLog/delete?id="+item.id,{id:item.id}).then((res) => {
toast.success('删除成功~')
paging.value.reload()
})
}
}
// go
const handleAdd = () => {
router.push({
name: 'AppletTtsPlayLogForm'
})
}
//go
const handleEdit = (record) => {
router.push({
name: 'AppletTtsPlayLogForm',
params: {dataId: record.id},
})
}
onMounted(() => {
//
uni.$on('refreshList', () => {
queryList(1,10)
})
})
</script>
<style lang="scss" scoped>
.wrap {
height: 100%;
}
:deep(.wd-swipe-action) {
margin-top: 10px;
background-color: #fff;
}
.list {
padding: 10px 10px;
width: 100%;
text-align: left;
display: flex;
justify-content: space-between;
.box {
width: 33%;
.field {
margin-bottom: 10px;
line-height: 20px;
}
}
}
.action {
width: 60px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.button {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
height: 100%;
color: #fff;
&:first-child {
background-color: #fa4350;
}
}
}
.add {
height: 70upx;
width: 70upx;
text-align: center;
line-height: 70upx;
background-color: #fff;
border-radius: 50%;
position: fixed;
bottom: 80upx;
right: 30upx;
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1);
color: #666;
}
</style>

+ 64
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/AppletTtsPlayLog.api.ts View File

@ -0,0 +1,64 @@
import {defHttp} from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/appletTtsPlayLog/appletTtsPlayLog/list',
save='/appletTtsPlayLog/appletTtsPlayLog/add',
edit='/appletTtsPlayLog/appletTtsPlayLog/edit',
deleteOne = '/appletTtsPlayLog/appletTtsPlayLog/delete',
deleteBatch = '/appletTtsPlayLog/appletTtsPlayLog/deleteBatch',
importExcel = '/appletTtsPlayLog/appletTtsPlayLog/importExcel',
exportXls = '/appletTtsPlayLog/appletTtsPlayLog/exportXls',
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
*
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}

+ 122
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/AppletTtsPlayLog.data.ts View File

@ -0,0 +1,122 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import { getWeekMonthQuarterYear } from '/@/utils';
//列表数据
export const columns: BasicColumn[] = [
{
title: '用户',
align:"center",
dataIndex: 'userId'
},
{
title: '文本',
align:"center",
dataIndex: 'text'
},
{
title: '音色',
align:"center",
dataIndex: 'voicetype'
},
{
title: '音量',
align:"center",
dataIndex: 'volume'
},
{
title: '语速',
align:"center",
dataIndex: 'speed'
},
{
title: '创建日期',
align:"center",
dataIndex: 'createTime'
},
{
title: '耗时',
align:"center",
dataIndex: 'elapsedTime'
},
{
title: '是否成功',
align:"center",
dataIndex: 'success',
customRender:({text}) => {
return render.renderSwitch(text, [{text:'是',value:'Y'},{text:'否',value:'N'}])
},
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '用户',
field: 'userId',
component: 'Input',
},
{
label: '文本',
field: 'text',
component: 'Input',
},
{
label: '音色',
field: 'voicetype',
component: 'InputNumber',
},
{
label: '音量',
field: 'volume',
component: 'InputNumber',
},
{
label: '语速',
field: 'speed',
component: 'InputNumber',
},
{
label: '耗时',
field: 'elapsedTime',
component: 'InputNumber',
},
{
label: '是否成功',
field: 'success',
component: 'JSwitch',
componentProps:{
},
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false
},
];
// 高级查询数据
export const superQuerySchema = {
userId: {title: '用户',order: 0,view: 'text', type: 'string',},
text: {title: '文本',order: 1,view: 'text', type: 'string',},
voicetype: {title: '音色',order: 2,view: 'number', type: 'number',},
volume: {title: '音量',order: 3,view: 'number', type: 'number',},
speed: {title: '语速',order: 4,view: 'number', type: 'number',},
createTime: {title: '创建日期',order: 5,view: 'datetime', type: 'string',},
elapsedTime: {title: '耗时',order: 6,view: 'number', type: 'number',},
success: {title: '是否成功',order: 7,view: 'switch', type: 'string',},
};
/**
* formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[]{
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}

+ 206
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/AppletTtsPlayLogList.vue View File

@ -0,0 +1,206 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="'appletTtsPlayLog:applet_tts_play_log:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" v-auth="'appletTtsPlayLog:applet_tts_play_log:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'appletTtsPlayLog:applet_tts_play_log:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button v-auth="'appletTtsPlayLog:applet_tts_play_log:deleteBatch'">批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
<!-- 高级查询 -->
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template v-slot:bodyCell="{ column, record, index, text }">
</template>
</BasicTable>
<!-- 表单区域 -->
<AppletTtsPlayLogModal @register="registerModal" @success="handleSuccess"></AppletTtsPlayLogModal>
</div>
</template>
<script lang="ts" name="appletTtsPlayLog-appletTtsPlayLog" setup>
import {ref, reactive, computed, unref} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import {useModal} from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage'
import AppletTtsPlayLogModal from './components/AppletTtsPlayLogModal.vue'
import {columns, searchFormSchema, superQuerySchema} from './AppletTtsPlayLog.data';
import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './AppletTtsPlayLog.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils';
//
const fieldPickers = reactive({
});
const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
const userStore = useUserStore();
const { createMessage } = useMessage();
//model
const [registerModal, {openModal}] = useModal();
//table
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '小程序语音朗读记录',
api: list,
columns,
canResize:true,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter:true,
showAdvancedButton:true,
fieldMapToNumber: [
],
fieldMapToTime: [
],
},
actionColumn: {
width: 120,
fixed:'right'
},
beforeFetch: (params) => {
if (params && fieldPickers) {
for (let key in fieldPickers) {
if (params[key]) {
params[key] = getDateByPicker(params[key], fieldPickers[key]);
}
}
}
return Object.assign(params, queryParam);
},
},
exportConfig: {
name:"小程序语音朗读记录",
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess
},
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
//
const superQueryConfig = reactive(superQuerySchema);
/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'appletTtsPlayLog:applet_tts_play_log:edit'
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
auth: 'appletTtsPlayLog:applet_tts_play_log:delete'
}
]
}
</script>
<style lang="less" scoped>
:deep(.ant-picker),:deep(.ant-input-number){
width: 100%;
}
</style>

+ 26
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/V20250912_1__menu_insert_AppletTtsPlayLog.sql View File

@ -0,0 +1,26 @@
-- 注意:该页面对应的前台目录为views/appletTtsPlayLog文件夹下
-- 如果你想更改到其他目录,请修改sql中component字段对应的值
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
VALUES ('2025091205466740370', NULL, '小程序语音朗读记录', '/appletTtsPlayLog/appletTtsPlayLogList', 'appletTtsPlayLog/AppletTtsPlayLogList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-09-12 17:46:37', NULL, NULL, 0);
-- 权限控制sql
-- 新增
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205466740371', '2025091205466740370', '添加小程序语音朗读记录', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsPlayLog:applet_tts_play_log:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:46:37', NULL, NULL, 0, 0, '1', 0);
-- 编辑
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205466740372', '2025091205466740370', '编辑小程序语音朗读记录', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsPlayLog:applet_tts_play_log:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:46:37', NULL, NULL, 0, 0, '1', 0);
-- 删除
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205466740373', '2025091205466740370', '删除小程序语音朗读记录', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsPlayLog:applet_tts_play_log:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:46:37', NULL, NULL, 0, 0, '1', 0);
-- 批量删除
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205466740374', '2025091205466740370', '批量删除小程序语音朗读记录', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsPlayLog:applet_tts_play_log:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:46:37', NULL, NULL, 0, 0, '1', 0);
-- 导出excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205466740375', '2025091205466740370', '导出excel_小程序语音朗读记录', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsPlayLog:applet_tts_play_log:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:46:37', NULL, NULL, 0, 0, '1', 0);
-- 导入excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205466740376', '2025091205466740370', '导入excel_小程序语音朗读记录', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsPlayLog:applet_tts_play_log:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:46:37', NULL, NULL, 0, 0, '1', 0);

+ 70
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/components/AppletTtsPlayLogForm.vue View File

@ -0,0 +1,70 @@
<template>
<div style="min-height: 400px">
<BasicForm @register="registerForm"></BasicForm>
<div style="width: 100%;text-align: center" v-if="!formDisabled">
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary"> </a-button>
</div>
</div>
</template>
<script lang="ts">
import {BasicForm, useForm} from '/@/components/Form/index';
import {computed, defineComponent} from 'vue';
import {defHttp} from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
import {getBpmFormSchema} from '../AppletTtsPlayLog.data';
import {saveOrUpdate} from '../AppletTtsPlayLog.api';
export default defineComponent({
name: "AppletTtsPlayLogForm",
components:{
BasicForm
},
props:{
formData: propTypes.object.def({}),
formBpm: propTypes.bool.def(true),
},
setup(props){
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
schemas: getBpmFormSchema(props.formData),
showActionButtonGroup: false,
baseColProps: {span: 24}
});
const formDisabled = computed(()=>{
if(props.formData.disabled === false){
return false;
}
return true;
});
let formData = {};
const queryByIdUrl = '/appletTtsPlayLog/appletTtsPlayLog/queryById';
async function initFormData(){
let params = {id: props.formData.dataId};
const data = await defHttp.get({url: queryByIdUrl, params});
formData = {...data}
//
await setFieldsValue(formData);
//
await setProps({disabled: formDisabled.value})
}
async function submitForm() {
let data = getFieldsValue();
let params = Object.assign({}, formData, data);
console.log('表单数据', params)
await saveOrUpdate(params, true)
}
initFormData();
return {
registerForm,
formDisabled,
submitForm,
}
}
});
</script>

+ 99
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsPlayLog/vue3/components/AppletTtsPlayLogModal.vue View File

@ -0,0 +1,99 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
<BasicForm @register="registerForm" name="AppletTtsPlayLogForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref, reactive} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {formSchema} from '../AppletTtsPlayLog.data';
import {saveOrUpdate} from '../AppletTtsPlayLog.api';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils';
const { createMessage } = useMessage();
// Emits
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
const isDetail = ref(false);
//
const [registerForm, { setProps,resetFields, setFieldsValue, validate, scrollToField }] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: {span: 24}
});
//
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
isDetail.value = !!data?.showFooter;
if (unref(isUpdate)) {
//
await setFieldsValue({
...data.record,
});
}
//
setProps({ disabled: !data?.showFooter })
});
//
const fieldPickers = reactive({
});
//
const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));
//
async function handleSubmit(v) {
try {
let values = await validate();
//
changeDateValue(values);
setModalProps({confirmLoading: true});
//
await saveOrUpdate(values, isUpdate.value);
//
closeModal();
//
emit('success');
} catch ({ errorFields }) {
if (errorFields) {
const firstField = errorFields[0];
if (firstField) {
scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
}
}
return Promise.reject(errorFields);
} finally {
setModalProps({confirmLoading: false});
}
}
/**
* 处理日期值
* @param formData 表单数据
*/
const changeDateValue = (formData) => {
if (formData && fieldPickers) {
for (let key in fieldPickers) {
if (formData[key]) {
formData[key] = getDateByPicker(formData[key], fieldPickers[key]);
}
}
}
};
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>

+ 182
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/controller/AppletTtsTimbreController.java View File

@ -0,0 +1,182 @@
package org.jeecg.modules.demo.appletTtsTimbre.controller;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre;
import org.jeecg.modules.demo.appletTtsTimbre.service.IAppletTtsTimbreService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.def.NormalExcelConstants;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.apache.shiro.authz.annotation.RequiresPermissions;
/**
* @Description: 小程序语音音色
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
@Tag(name="小程序语音音色")
@RestController
@RequestMapping("/appletTtsTimbre/appletTtsTimbre")
@Slf4j
public class AppletTtsTimbreController extends JeecgController<AppletTtsTimbre, IAppletTtsTimbreService> {
@Autowired
private IAppletTtsTimbreService appletTtsTimbreService;
/**
* 分页列表查询
*
* @param appletTtsTimbre
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "小程序语音音色-分页列表查询")
@Operation(summary="小程序语音音色-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<AppletTtsTimbre>> queryPageList(AppletTtsTimbre appletTtsTimbre,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<AppletTtsTimbre> queryWrapper = QueryGenerator.initQueryWrapper(appletTtsTimbre, req.getParameterMap());
Page<AppletTtsTimbre> page = new Page<AppletTtsTimbre>(pageNo, pageSize);
IPage<AppletTtsTimbre> pageList = appletTtsTimbreService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
* 添加
*
* @param appletTtsTimbre
* @return
*/
@AutoLog(value = "小程序语音音色-添加")
@Operation(summary="小程序语音音色-添加")
@RequiresPermissions("appletTtsTimbre:applet_tts_timbre:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody AppletTtsTimbre appletTtsTimbre) {
appletTtsTimbreService.save(appletTtsTimbre);
return Result.OK("添加成功!");
}
/**
* 编辑
*
* @param appletTtsTimbre
* @return
*/
@AutoLog(value = "小程序语音音色-编辑")
@Operation(summary="小程序语音音色-编辑")
@RequiresPermissions("appletTtsTimbre:applet_tts_timbre:edit")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<String> edit(@RequestBody AppletTtsTimbre appletTtsTimbre) {
appletTtsTimbreService.updateById(appletTtsTimbre);
return Result.OK("编辑成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@AutoLog(value = "小程序语音音色-通过id删除")
@Operation(summary="小程序语音音色-通过id删除")
@RequiresPermissions("appletTtsTimbre:applet_tts_timbre:delete")
@DeleteMapping(value = "/delete")
public Result<String> delete(@RequestParam(name="id",required=true) String id) {
appletTtsTimbreService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@AutoLog(value = "小程序语音音色-批量删除")
@Operation(summary="小程序语音音色-批量删除")
@RequiresPermissions("appletTtsTimbre:applet_tts_timbre:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
this.appletTtsTimbreService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
//@AutoLog(value = "小程序语音音色-通过id查询")
@Operation(summary="小程序语音音色-通过id查询")
@GetMapping(value = "/queryById")
public Result<AppletTtsTimbre> queryById(@RequestParam(name="id",required=true) String id) {
AppletTtsTimbre appletTtsTimbre = appletTtsTimbreService.getById(id);
if(appletTtsTimbre==null) {
return Result.error("未找到对应数据");
}
return Result.OK(appletTtsTimbre);
}
/**
* 导出excel
*
* @param request
* @param appletTtsTimbre
*/
@RequiresPermissions("appletTtsTimbre:applet_tts_timbre:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, AppletTtsTimbre appletTtsTimbre) {
return super.exportXls(request, appletTtsTimbre, AppletTtsTimbre.class, "小程序语音音色");
}
/**
* 通过excel导入数据
*
* @param request
* @param response
* @return
*/
@RequiresPermissions("appletTtsTimbre:applet_tts_timbre:importExcel")
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) {
return super.importExcel(request, response, AppletTtsTimbre.class);
}
}

+ 71
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/entity/AppletTtsTimbre.java View File

@ -0,0 +1,71 @@
package org.jeecg.modules.demo.appletTtsTimbre.entity;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.TableLogic;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.springframework.format.annotation.DateTimeFormat;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.jeecg.common.aspect.annotation.Dict;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @Description: 小程序语音音色
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
@Data
@TableName("applet_tts_timbre")
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
@Schema(description="小程序语音音色")
public class AppletTtsTimbre implements Serializable {
private static final long serialVersionUID = 1L;
/**主键*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
/**创建人*/
@Schema(description = "创建人")
private java.lang.String createBy;
/**创建日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "创建日期")
private java.util.Date createTime;
/**更新人*/
@Schema(description = "更新人")
private java.lang.String updateBy;
/**更新日期*/
@JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@Schema(description = "更新日期")
private java.util.Date updateTime;
/**所属部门*/
@Schema(description = "所属部门")
private java.lang.String sysOrgCode;
/**名称*/
@Excel(name = "名称", width = 15)
@Schema(description = "名称")
private java.lang.String name;
/**音色ID*/
@Excel(name = "音色ID", width = 15)
@Schema(description = "音色ID")
private java.lang.Integer voiceType;
/**头像*/
@Excel(name = "头像", width = 15)
@Schema(description = "头像")
private java.lang.String image;
}

+ 17
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/mapper/AppletTtsTimbreMapper.java View File

@ -0,0 +1,17 @@
package org.jeecg.modules.demo.appletTtsTimbre.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @Description: 小程序语音音色
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
public interface AppletTtsTimbreMapper extends BaseMapper<AppletTtsTimbre> {
}

+ 5
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/mapper/xml/AppletTtsTimbreMapper.xml View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.demo.appletTtsTimbre.mapper.AppletTtsTimbreMapper">
</mapper>

+ 14
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/service/IAppletTtsTimbreService.java View File

@ -0,0 +1,14 @@
package org.jeecg.modules.demo.appletTtsTimbre.service;
import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @Description: 小程序语音音色
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
public interface IAppletTtsTimbreService extends IService<AppletTtsTimbre> {
}

+ 19
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/service/impl/AppletTtsTimbreServiceImpl.java View File

@ -0,0 +1,19 @@
package org.jeecg.modules.demo.appletTtsTimbre.service.impl;
import org.jeecg.modules.demo.appletTtsTimbre.entity.AppletTtsTimbre;
import org.jeecg.modules.demo.appletTtsTimbre.mapper.AppletTtsTimbreMapper;
import org.jeecg.modules.demo.appletTtsTimbre.service.IAppletTtsTimbreService;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
/**
* @Description: 小程序语音音色
* @Author: jeecg-boot
* @Date: 2025-09-12
* @Version: V1.0
*/
@Service
public class AppletTtsTimbreServiceImpl extends ServiceImpl<AppletTtsTimbreMapper, AppletTtsTimbre> implements IAppletTtsTimbreService {
}

+ 95
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp/AppletTtsTimbreForm.vue View File

@ -0,0 +1,95 @@
<template>
<view>
<!--标题和返回-->
<cu-custom :bgColor="NavBarColor" isBack :backRouterName="backRouteName">
<block slot="backText">返回</block>
<block slot="content">小程序语音音色</block>
</cu-custom>
<!--表单区域-->
<view>
<form>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">名称</text></view>
<input placeholder="请输入名称" v-model="model.name"/>
</view>
</view>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">音色ID</text></view>
<input type="number" placeholder="请输入音色ID" v-model="model.voiceType"/>
</view>
</view>
<view class="cu-form-group">
<view class="flex align-center">
<view class="title"><text space="ensp">头像</text></view>
<input placeholder="请输入头像" v-model="model.image"/>
</view>
</view>
<view class="padding">
<button class="cu-btn block bg-blue margin-tb-sm lg" @click="onSubmit">
<text v-if="loading" class="cuIcon-loading2 cuIconfont-spin"></text>提交
</button>
</view>
</form>
</view>
</view>
</template>
<script>
import myDate from '@/components/my-componets/my-date.vue'
export default {
name: "AppletTtsTimbreForm",
components:{ myDate },
props:{
formData:{
type:Object,
default:()=>{},
required:false
}
},
data(){
return {
CustomBar: this.CustomBar,
NavBarColor: this.NavBarColor,
loading:false,
model: {},
backRouteName:'index',
url: {
queryById: "/appletTtsTimbre/appletTtsTimbre/queryById",
add: "/appletTtsTimbre/appletTtsTimbre/add",
edit: "/appletTtsTimbre/appletTtsTimbre/edit",
},
}
},
created(){
this.initFormData();
},
methods:{
initFormData(){
if(this.formData){
let dataId = this.formData.dataId;
this.$http.get(this.url.queryById,{params:{id:dataId}}).then((res)=>{
if(res.data.success){
console.log("表单数据",res);
this.model = res.data.result;
}
})
}
},
onSubmit() {
let myForm = {...this.model};
this.loading = true;
let url = myForm.id?this.url.edit:this.url.add;
this.$http.post(url,myForm).then(res=>{
console.log("res",res)
this.loading = false
this.$Router.push({name:this.backRouteName})
}).catch(()=>{
this.loading = false
});
}
}
}
</script>

+ 44
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp/AppletTtsTimbreList.vue View File

@ -0,0 +1,44 @@
<template>
<view>
<!--标题和返回-->
<cu-custom :bgColor="NavBarColor" isBack>
<block slot="backText">返回</block>
<block slot="content">小程序语音音色</block>
</cu-custom>
<!--滚动加载列表-->
<mescroll-body ref="mescrollRef" bottom="88" @init="mescrollInit" :up="upOption" :down="downOption" @down="downCallback" @up="upCallback">
<view class="cu-list menu">
<view class="cu-item" v-for="(item,index) in list" :key="index" @click="goHome">
<view class="flex" style="width:100%">
<text class="text-lg" style="color: #000;">
{{ item.createBy}}
</text>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import MescrollMixin from "@/components/mescroll-uni/mescroll-mixins.js";
import Mixin from "@/common/mixin/Mixin.js";
export default {
name: '小程序语音音色',
mixins: [MescrollMixin,Mixin],
data() {
return {
CustomBar:this.CustomBar,
NavBarColor:this.NavBarColor,
url: "/appletTtsTimbre/appletTtsTimbre/list",
};
},
methods: {
goHome(){
this.$Router.push({name: "index"})
}
}
}
</script>

+ 19
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp3/AppletTtsTimbreData.ts View File

@ -0,0 +1,19 @@
import { render } from '@/common/renderUtils';
//列表数据
export const columns = [
{
title: '名称',
align:"center",
dataIndex: 'name'
},
{
title: '音色ID',
align:"center",
dataIndex: 'voiceType'
},
{
title: '头像',
align:"center",
dataIndex: 'image'
},
];

+ 236
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp3/AppletTtsTimbreForm.vue View File

@ -0,0 +1,236 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationStyle: 'custom',
navigationBarTitleText: '小程序语音音色',
},
}
</route>
<template>
<PageLayout :navTitle="navTitle" :backRouteName="backRouteName">
<scroll-view class="scrollArea" scroll-y>
<view class="form-container">
<wd-form ref="form" :model="myFormData">
<wd-cell-group border>
<view class="{ 'mt-14px': 0 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['name']"
:label="get4Label('名称')"
name='name'
prop='name'
placeholder="请选择名称"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 1 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['voiceType']"
:label="get4Label('音色ID')"
name='voiceType'
prop='voiceType'
placeholder="请选择音色ID"
inputMode="numeric"
:rules="[
]"
clearable
/>
</view>
<view class="{ 'mt-14px': 0 == 0 }">
<wd-input
label-width="100px"
v-model="myFormData['image']"
:label="get4Label('头像')"
name='image'
prop='image'
placeholder="请选择头像"
:rules="[
]"
clearable
/>
</view>
</wd-cell-group>
</wd-form>
</view>
</scroll-view>
<view class="footer">
<wd-button :disabled="loading" block :loading="loading" @click="handleSubmit">提交</wd-button>
</view>
</PageLayout>
</template>
<script lang="ts" setup>
import { onLoad } from '@dcloudio/uni-app'
import { http } from '@/utils/http'
import { useToast } from 'wot-design-uni'
import { useRouter } from '@/plugin/uni-mini-router'
import { ref, onMounted, computed,reactive } from 'vue'
import OnlineImage from '@/components/online/view/online-image.vue'
import OnlineFile from '@/components/online/view/online-file.vue'
import OnlineFileCustom from '@/components/online/view/online-file-custom.vue'
import OnlineSelect from '@/components/online/view/online-select.vue'
import OnlineTime from '@/components/online/view/online-time.vue'
import OnlineDate from '@/components/online/view/online-date.vue'
import OnlineRadio from '@/components/online/view/online-radio.vue'
import OnlineCheckbox from '@/components/online/view/online-checkbox.vue'
import OnlineMulti from '@/components/online/view/online-multi.vue'
import OnlinePopupLinkRecord from '@/components/online/view/online-popup-link-record.vue'
import OnlinePca from '@/components/online/view/online-pca.vue'
import SelectDept from '@/components/SelectDept/SelectDept.vue'
import SelectUser from '@/components/SelectUser/SelectUser.vue'
import {duplicateCheck} from "@/service/api";
defineOptions({
name: 'AppletTtsTimbreForm',
options: {
styleIsolation: 'shared',
},
})
const toast = useToast()
const router = useRouter()
const form = ref(null)
//
const myFormData = reactive({})
const loading = ref(false)
const navTitle = ref('新增')
const dataId = ref('')
const backRouteName = ref('AppletTtsTimbreList')
// initForm
const initForm = (item) => {
console.log('initForm item', item)
if(item?.dataId){
dataId.value = item.dataId;
navTitle.value = item.dataId?'编辑':'新增';
initData();
}
}
//
const initData = () => {
http.get("/appletTtsTimbre/appletTtsTimbre/queryById",{id:dataId.value}).then((res) => {
if (res.success) {
let obj = res.result
Object.assign(myFormData, { ...obj })
}else{
toast.error(res?.message || '表单加载失败!')
}
})
}
const handleSuccess = () => {
uni.$emit('refreshList');
router.back()
}
/**
* 校验唯一
* @param values
* @returns {boolean}
*/
async function fieldCheck(values: any) {
const onlyField = [
];
for (const field of onlyField) {
if (values[field]) {
//
const res: any = await duplicateCheck({
tableName: 'applet_tts_timbre',
fieldName: field, // 使
fieldVal: values[field],
dataId: values.id,
});
if (!res.success) {
toast.warning(res.message);
return true; //
}
}
}
return false; //
}
//
const handleSubmit = async () => {
//
if (await fieldCheck(myFormData)) {
return
}
let url = dataId.value?'/appletTtsTimbre/appletTtsTimbre/edit':'/appletTtsTimbre/appletTtsTimbre/add';
form.value
.validate()
.then(({ valid, errors }) => {
if (valid) {
loading.value = true;
http.post(url,myFormData).then((res) => {
loading.value = false;
if (res.success) {
toast.success('保存成功');
handleSuccess()
}else{
toast.error(res?.message || '表单保存失败!')
}
})
}
})
.catch((error) => {
console.log(error, 'error')
loading.value = false;
})
}
//
const get4Label = computed(() => {
return (label) => {
return label && label.length > 4 ? label.substring(0, 4) : label;
}
})
//
const getFormSchema = computed(() => {
return (dictTable,dictCode,dictText) => {
return {
dictCode,
dictTable,
dictText
};
}
})
/**
* 获取日期控件的扩展类型
* @param picker
* @returns {string}
*/
const getDateExtendType = (picker: string) => {
let mapField = {
month: 'year-month',
year: 'year',
quarter: 'quarter',
week: 'week',
day: 'date',
}
return picker && mapField[picker]
? mapField[picker]
: 'date'
}
//pop
const setFieldsValue = (data) => {
Object.assign(myFormData, {...data })
}
// onLoad
onLoad((option) => {
initForm(option)
})
</script>
<style lang="scss" scoped>
.footer {
width: 100%;
padding: 10px 20px;
padding-bottom: calc(constant(safe-area-inset-bottom) + 10px);
padding-bottom: calc(env(safe-area-inset-bottom) + 10px);
}
:deep(.wd-cell__label) {
font-size: 14px;
color: #444;
}
:deep(.wd-cell__value) {
text-align: left;
}
</style>

+ 148
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/uniapp3/AppletTtsTimbreList.vue View File

@ -0,0 +1,148 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationBarTitleText: '小程序语音音色',
navigationStyle: 'custom',
},
}
</route>
<template>
<PageLayout navTitle="小程序语音音色" backRouteName="index" routeMethod="pushTab">
<view class="wrap">
<z-paging
ref="paging"
:fixed="false"
v-model="dataList"
@query="queryList"
:default-page-size="15"
>
<template v-for="item in dataList" :key="item.id">
<wd-swipe-action>
<view class="list" @click="handleEdit(item)">
<template v-for="(cItem, cIndex) in columns" :key="cIndex">
<view v-if="cIndex < 3" class="box" :style="getBoxStyle">
<view class="field ellipsis">{{ cItem.title }}</view>
<view class="value cu-text-grey">{{ item[cItem.dataIndex] }}</view>
</view>
</template>
</view>
<template #right>
<view class="action">
<view class="button" @click="handleAction('del', item)">删除</view>
</view>
</template>
</wd-swipe-action>
</template>
</z-paging>
<view class="add u-iconfont u-icon-add" @click="handleAdd"></view>
</view>
</PageLayout>
</template>
<script setup lang="ts">
import { ref, onMounted, computed } from 'vue'
import { http } from '@/utils/http'
import usePageList from '@/hooks/usePageList'
import {columns} from './AppletTtsTimbreData';
defineOptions({
name: 'AppletTtsTimbreList',
options: {
styleIsolation: 'shared',
}
})
//
let { toast, router, paging, dataList, queryList } = usePageList('/appletTtsTimbre/appletTtsTimbre/list');
//
const getBoxStyle = computed(() => {
return { width: "calc(33% - 5px)" }
})
//
const handleAction = (val, item) => {
if (val == 'del') {
http.delete("/appletTtsTimbre/appletTtsTimbre/delete?id="+item.id,{id:item.id}).then((res) => {
toast.success('删除成功~')
paging.value.reload()
})
}
}
// go
const handleAdd = () => {
router.push({
name: 'AppletTtsTimbreForm'
})
}
//go
const handleEdit = (record) => {
router.push({
name: 'AppletTtsTimbreForm',
params: {dataId: record.id},
})
}
onMounted(() => {
//
uni.$on('refreshList', () => {
queryList(1,10)
})
})
</script>
<style lang="scss" scoped>
.wrap {
height: 100%;
}
:deep(.wd-swipe-action) {
margin-top: 10px;
background-color: #fff;
}
.list {
padding: 10px 10px;
width: 100%;
text-align: left;
display: flex;
justify-content: space-between;
.box {
width: 33%;
.field {
margin-bottom: 10px;
line-height: 20px;
}
}
}
.action {
width: 60px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
.button {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
height: 100%;
color: #fff;
&:first-child {
background-color: #fa4350;
}
}
}
.add {
height: 70upx;
width: 70upx;
text-align: center;
line-height: 70upx;
background-color: #fff;
border-radius: 50%;
position: fixed;
bottom: 80upx;
right: 30upx;
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1);
color: #666;
}
</style>

+ 64
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/AppletTtsTimbre.api.ts View File

@ -0,0 +1,64 @@
import {defHttp} from '/@/utils/http/axios';
import { useMessage } from "/@/hooks/web/useMessage";
const { createConfirm } = useMessage();
enum Api {
list = '/appletTtsTimbre/appletTtsTimbre/list',
save='/appletTtsTimbre/appletTtsTimbre/add',
edit='/appletTtsTimbre/appletTtsTimbre/edit',
deleteOne = '/appletTtsTimbre/appletTtsTimbre/delete',
deleteBatch = '/appletTtsTimbre/appletTtsTimbre/deleteBatch',
importExcel = '/appletTtsTimbre/appletTtsTimbre/importExcel',
exportXls = '/appletTtsTimbre/appletTtsTimbre/exportXls',
}
/**
* api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* api
*/
export const getImportUrl = Api.importExcel;
/**
*
* @param params
*/
export const list = (params) =>
defHttp.get({url: Api.list, params});
/**
*
*/
export const deleteOne = (params,handleSuccess) => {
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
/**
*
* @param params
*/
export const batchDelete = (params, handleSuccess) => {
createConfirm({
iconType: 'warning',
title: '确认删除',
content: '是否删除选中数据',
okText: '确认',
cancelText: '取消',
onOk: () => {
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
handleSuccess();
});
}
});
}
/**
*
* @param params
*/
export const saveOrUpdate = (params, isUpdate) => {
let url = isUpdate ? Api.edit : Api.save;
return defHttp.post({url: url, params});
}

+ 67
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/AppletTtsTimbre.data.ts View File

@ -0,0 +1,67 @@
import {BasicColumn} from '/@/components/Table';
import {FormSchema} from '/@/components/Table';
import { rules} from '/@/utils/helper/validator';
import { render } from '/@/utils/common/renderUtils';
import { getWeekMonthQuarterYear } from '/@/utils';
//列表数据
export const columns: BasicColumn[] = [
{
title: '名称',
align:"center",
dataIndex: 'name'
},
{
title: '音色ID',
align:"center",
dataIndex: 'voiceType'
},
{
title: '头像',
align:"center",
dataIndex: 'image'
},
];
//查询数据
export const searchFormSchema: FormSchema[] = [
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '名称',
field: 'name',
component: 'Input',
},
{
label: '音色ID',
field: 'voiceType',
component: 'InputNumber',
},
{
label: '头像',
field: 'image',
component: 'Input',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
component: 'Input',
show: false
},
];
// 高级查询数据
export const superQuerySchema = {
name: {title: '名称',order: 0,view: 'text', type: 'string',},
voiceType: {title: '音色ID',order: 1,view: 'number', type: 'number',},
image: {title: '头像',order: 2,view: 'text', type: 'string',},
};
/**
* formSchema
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[]{
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}

+ 206
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/AppletTtsTimbreList.vue View File

@ -0,0 +1,206 @@
<template>
<div>
<!--引用表格-->
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<!--插槽:table标题-->
<template #tableTitle>
<a-button type="primary" v-auth="'appletTtsTimbre:applet_tts_timbre:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" v-auth="'appletTtsTimbre:applet_tts_timbre:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'appletTtsTimbre:applet_tts_timbre:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<template #overlay>
<a-menu>
<a-menu-item key="1" @click="batchHandleDelete">
<Icon icon="ant-design:delete-outlined"></Icon>
删除
</a-menu-item>
</a-menu>
</template>
<a-button v-auth="'appletTtsTimbre:applet_tts_timbre:deleteBatch'">批量操作
<Icon icon="mdi:chevron-down"></Icon>
</a-button>
</a-dropdown>
<!-- 高级查询 -->
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
</template>
<!--操作栏-->
<template #action="{ record }">
<TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)"/>
</template>
<!--字段回显插槽-->
<template v-slot:bodyCell="{ column, record, index, text }">
</template>
</BasicTable>
<!-- 表单区域 -->
<AppletTtsTimbreModal @register="registerModal" @success="handleSuccess"></AppletTtsTimbreModal>
</div>
</template>
<script lang="ts" name="appletTtsTimbre-appletTtsTimbre" setup>
import {ref, reactive, computed, unref} from 'vue';
import {BasicTable, useTable, TableAction} from '/@/components/Table';
import {useModal} from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage'
import AppletTtsTimbreModal from './components/AppletTtsTimbreModal.vue'
import {columns, searchFormSchema, superQuerySchema} from './AppletTtsTimbre.data';
import {list, deleteOne, batchDelete, getImportUrl,getExportUrl} from './AppletTtsTimbre.api';
import { downloadFile } from '/@/utils/common/renderUtils';
import { useUserStore } from '/@/store/modules/user';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils';
//
const fieldPickers = reactive({
});
const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
const userStore = useUserStore();
const { createMessage } = useMessage();
//model
const [registerModal, {openModal}] = useModal();
//table
const { prefixCls,tableContext,onExportXls,onImportXls } = useListPage({
tableProps:{
title: '小程序语音音色',
api: list,
columns,
canResize:true,
formConfig: {
//labelWidth: 120,
schemas: searchFormSchema,
autoSubmitOnEnter:true,
showAdvancedButton:true,
fieldMapToNumber: [
],
fieldMapToTime: [
],
},
actionColumn: {
width: 120,
fixed:'right'
},
beforeFetch: (params) => {
if (params && fieldPickers) {
for (let key in fieldPickers) {
if (params[key]) {
params[key] = getDateByPicker(params[key], fieldPickers[key]);
}
}
}
return Object.assign(params, queryParam);
},
},
exportConfig: {
name:"小程序语音音色",
url: getExportUrl,
params: queryParam,
},
importConfig: {
url: getImportUrl,
success: handleSuccess
},
})
const [registerTable, {reload},{ rowSelection, selectedRowKeys }] = tableContext
//
const superQueryConfig = reactive(superQuerySchema);
/**
* 高级查询事件
*/
function handleSuperQuery(params) {
Object.keys(params).map((k) => {
queryParam[k] = params[k];
});
reload();
}
/**
* 新增事件
*/
function handleAdd() {
openModal(true, {
isUpdate: false,
showFooter: true,
});
}
/**
* 编辑事件
*/
function handleEdit(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: true,
});
}
/**
* 详情
*/
function handleDetail(record: Recordable) {
openModal(true, {
record,
isUpdate: true,
showFooter: false,
});
}
/**
* 删除事件
*/
async function handleDelete(record) {
await deleteOne({id: record.id}, handleSuccess);
}
/**
* 批量删除事件
*/
async function batchHandleDelete() {
await batchDelete({ids: selectedRowKeys.value}, handleSuccess);
}
/**
* 成功回调
*/
function handleSuccess() {
(selectedRowKeys.value = []) && reload();
}
/**
* 操作栏
*/
function getTableAction(record){
return [
{
label: '编辑',
onClick: handleEdit.bind(null, record),
auth: 'appletTtsTimbre:applet_tts_timbre:edit'
}
]
}
/**
* 下拉操作栏
*/
function getDropDownAction(record){
return [
{
label: '详情',
onClick: handleDetail.bind(null, record),
}, {
label: '删除',
popConfirm: {
title: '是否确认删除',
confirm: handleDelete.bind(null, record),
placement: 'topLeft',
},
auth: 'appletTtsTimbre:applet_tts_timbre:delete'
}
]
}
</script>
<style lang="less" scoped>
:deep(.ant-picker),:deep(.ant-input-number){
width: 100%;
}
</style>

+ 26
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/V20250912_1__menu_insert_AppletTtsTimbre.sql View File

@ -0,0 +1,26 @@
-- 注意:该页面对应的前台目录为views/appletTtsTimbre文件夹下
-- 如果你想更改到其他目录,请修改sql中component字段对应的值
INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external)
VALUES ('2025091205389400570', NULL, '小程序语音音色', '/appletTtsTimbre/appletTtsTimbreList', 'appletTtsTimbre/AppletTtsTimbreList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-09-12 17:38:57', NULL, NULL, 0);
-- 权限控制sql
-- 新增
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205389400571', '2025091205389400570', '添加小程序语音音色', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsTimbre:applet_tts_timbre:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:38:57', NULL, NULL, 0, 0, '1', 0);
-- 编辑
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205389400572', '2025091205389400570', '编辑小程序语音音色', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsTimbre:applet_tts_timbre:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:38:57', NULL, NULL, 0, 0, '1', 0);
-- 删除
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205389400573', '2025091205389400570', '删除小程序语音音色', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsTimbre:applet_tts_timbre:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:38:57', NULL, NULL, 0, 0, '1', 0);
-- 批量删除
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205389400574', '2025091205389400570', '批量删除小程序语音音色', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsTimbre:applet_tts_timbre:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:38:57', NULL, NULL, 0, 0, '1', 0);
-- 导出excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205389400575', '2025091205389400570', '导出excel_小程序语音音色', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsTimbre:applet_tts_timbre:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:38:57', NULL, NULL, 0, 0, '1', 0);
-- 导入excel
INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external)
VALUES ('2025091205389400576', '2025091205389400570', '导入excel_小程序语音音色', NULL, NULL, 0, NULL, NULL, 2, 'appletTtsTimbre:applet_tts_timbre:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-09-12 17:38:57', NULL, NULL, 0, 0, '1', 0);

+ 70
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/components/AppletTtsTimbreForm.vue View File

@ -0,0 +1,70 @@
<template>
<div style="min-height: 400px">
<BasicForm @register="registerForm"></BasicForm>
<div style="width: 100%;text-align: center" v-if="!formDisabled">
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary"> </a-button>
</div>
</div>
</template>
<script lang="ts">
import {BasicForm, useForm} from '/@/components/Form/index';
import {computed, defineComponent} from 'vue';
import {defHttp} from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
import {getBpmFormSchema} from '../AppletTtsTimbre.data';
import {saveOrUpdate} from '../AppletTtsTimbre.api';
export default defineComponent({
name: "AppletTtsTimbreForm",
components:{
BasicForm
},
props:{
formData: propTypes.object.def({}),
formBpm: propTypes.bool.def(true),
},
setup(props){
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
schemas: getBpmFormSchema(props.formData),
showActionButtonGroup: false,
baseColProps: {span: 24}
});
const formDisabled = computed(()=>{
if(props.formData.disabled === false){
return false;
}
return true;
});
let formData = {};
const queryByIdUrl = '/appletTtsTimbre/appletTtsTimbre/queryById';
async function initFormData(){
let params = {id: props.formData.dataId};
const data = await defHttp.get({url: queryByIdUrl, params});
formData = {...data}
//
await setFieldsValue(formData);
//
await setProps({disabled: formDisabled.value})
}
async function submitForm() {
let data = getFieldsValue();
let params = Object.assign({}, formData, data);
console.log('表单数据', params)
await saveOrUpdate(params, true)
}
initFormData();
return {
registerForm,
formDisabled,
submitForm,
}
}
});
</script>

+ 99
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletTtsTimbre/vue3/components/AppletTtsTimbreModal.vue View File

@ -0,0 +1,99 @@
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
<BasicForm @register="registerForm" name="AppletTtsTimbreForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref, reactive} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {formSchema} from '../AppletTtsTimbre.data';
import {saveOrUpdate} from '../AppletTtsTimbre.api';
import { useMessage } from '/@/hooks/web/useMessage';
import { getDateByPicker } from '/@/utils';
const { createMessage } = useMessage();
// Emits
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
const isDetail = ref(false);
//
const [registerForm, { setProps,resetFields, setFieldsValue, validate, scrollToField }] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: {span: 24}
});
//
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
isDetail.value = !!data?.showFooter;
if (unref(isUpdate)) {
//
await setFieldsValue({
...data.record,
});
}
//
setProps({ disabled: !data?.showFooter })
});
//
const fieldPickers = reactive({
});
//
const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));
//
async function handleSubmit(v) {
try {
let values = await validate();
//
changeDateValue(values);
setModalProps({confirmLoading: true});
//
await saveOrUpdate(values, isUpdate.value);
//
closeModal();
//
emit('success');
} catch ({ errorFields }) {
if (errorFields) {
const firstField = errorFields[0];
if (firstField) {
scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
}
}
return Promise.reject(errorFields);
} finally {
setModalProps({confirmLoading: false});
}
}
/**
* 处理日期值
* @param formData 表单数据
*/
const changeDateValue = (formData) => {
if (formData && fieldPickers) {
for (let key in fieldPickers) {
if (formData[key]) {
formData[key] = getDateByPicker(formData[key], fieldPickers[key]);
}
}
}
};
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>

+ 6
- 0
jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/resources/application.yml View File

@ -0,0 +1,6 @@
# 腾讯云配置
tencent:
# 腾讯云API密钥ID
secretId: your_secret_id_here
# 腾讯云API密钥Key
secretKey: your_secret_key_here

+ 1
- 0
jeecg-boot/jeecg-module-system/jeecg-system-start/pom.xml View File

@ -43,6 +43,7 @@
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
</dependencies>
<build>


+ 8
- 1
jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml View File

@ -338,4 +338,11 @@ wechat:
publicKeyId: PUB_KEY_ID_0117018416542025022100395100001649 #公钥
merchantSerialNumber: 4B672D1BCB20B22081FD6C5ECAA7C1277AF1B772 # 商户API证书序列号
apiV3Key: 0fdb77429ffdf206c151af76a663041c # 商户APIV3密钥
refundNotifyUrl: # 退款通知地址(正式环境)
refundNotifyUrl: # 退款通知地址(正式环境)
tencent:
secretId: AKIDDH7j7IFzgCUbUBfaJuJVTDk4jCVbT7Xm
secretKey: 4NURCj281g7RWP4Vj8KJ5dy5zf9PSIuN

Loading…
Cancel
Save