3 Коміти 07905b77ca ... 645fbdae73

Автор SHA1 Опис Дата
  叶静 645fbdae73 fix:bug 3 тижнів тому
  叶静 05c5deefe7 feat: 優化細節 4 тижнів тому
  叶静 54b94e46d0 feat:新增问答 4 тижнів тому

+ 1 - 1
.env.development

@@ -21,7 +21,7 @@ SHEEP_USE_MOCK = false
 # 代理配置
 SHEEP_USE_PROXY = false
 SHEEP_PROXY_PREFIX = '/api'
-SHEEP_PROXY_TARGET = http://192.168.0.103:8401
+SHEEP_PROXY_TARGET = http://192.168.0.104:8401
 
 ##### API路由配置
 # API路由功能总开关

+ 1586 - 0
docs/qa.json

@@ -0,0 +1,1586 @@
+{
+  "openapi": "3.0.1",
+  "info": {
+    "title": "默认模块",
+    "description": "",
+    "version": "1.0.0"
+  },
+  "tags": [],
+  "paths": {
+    "/qa/groupList": {
+      "get": {
+        "summary": "分类列表",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "page",
+            "in": "query",
+            "description": "第几页",
+            "required": false,
+            "schema": {
+              "type": "integer"
+            }
+          },
+          {
+            "name": "size",
+            "in": "query",
+            "description": "每页显示条数",
+            "required": false,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResultPageQaGroup"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "bnName": "",
+                            "enName": ""
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "bnName": "",
+                            "enName": ""
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  },
+                  "3": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "bnName": "",
+                            "enName": ""
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  },
+                  "4": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "bnName": "",
+                            "enName": ""
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  },
+                  "5": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "bnName": "",
+                            "enName": ""
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  },
+                  "6": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "bnName": "",
+                            "enName": "",
+                            "sort": 0
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  },
+                  "7": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "bnName": "",
+                            "enName": "",
+                            "sort": 0
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/add": {
+      "post": {
+        "summary": "新增",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/QaGroupRO",
+                "description": ""
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Result",
+                  "description": "统一API响应结果封装"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "3": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "4": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "5": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "6": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/update/{id:^\\d+$}": {
+      "post": {
+        "summary": "修改",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "",
+            "required": true,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/QaGroupRO",
+                "description": ""
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Result",
+                  "description": "统一API响应结果封装"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "3": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "4": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/disable/{id:^\\d+$}": {
+      "post": {
+        "summary": "禁用",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "",
+            "required": true,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Result",
+                  "description": "统一API响应结果封装"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/changeStatus/{id:^\\d+$}": {
+      "post": {
+        "summary": "修改状态",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "",
+            "required": true,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Result",
+                  "description": "统一API响应结果封装"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/availableGroupList": {
+      "get": {
+        "summary": "可用分组列表",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResultListQaGroup"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": [
+                        {
+                          "id": 0,
+                          "createUser": "",
+                          "updateUser": "",
+                          "updateTime": "",
+                          "createTime": "",
+                          "delFlag": false,
+                          "bnName": "",
+                          "enName": ""
+                        }
+                      ]
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": [
+                        {
+                          "id": 0,
+                          "createUser": "",
+                          "updateUser": "",
+                          "updateTime": "",
+                          "createTime": "",
+                          "delFlag": false,
+                          "bnName": "",
+                          "enName": "",
+                          "sort": 0
+                        }
+                      ]
+                    }
+                  },
+                  "3": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": [
+                        {
+                          "id": 0,
+                          "createUser": "",
+                          "updateUser": "",
+                          "updateTime": "",
+                          "createTime": "",
+                          "delFlag": false,
+                          "bnName": "",
+                          "enName": "",
+                          "sort": 0
+                        }
+                      ]
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/qaList": {
+      "get": {
+        "summary": "qa列表",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "page",
+            "in": "query",
+            "description": "第几页",
+            "required": false,
+            "schema": {
+              "type": "integer"
+            }
+          },
+          {
+            "name": "size",
+            "in": "query",
+            "description": "每页显示条数",
+            "required": false,
+            "schema": {
+              "type": "integer"
+            }
+          },
+          {
+            "name": "groupId",
+            "in": "query",
+            "description": "分类ID",
+            "required": false,
+            "schema": {
+              "type": "integer",
+              "format": "int64"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResultPageQaListVO"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "bnTitle": "",
+                            "enTitle": "",
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "groupDelFlag": false,
+                            "groupId": 0,
+                            "groupBnName": "",
+                            "groupEnName": "",
+                            "sort": 0
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "",
+                      "message": "",
+                      "data": {
+                        "records": [
+                          {
+                            "id": 0,
+                            "bnTitle": "",
+                            "enTitle": "",
+                            "createUser": "",
+                            "updateUser": "",
+                            "updateTime": "",
+                            "createTime": "",
+                            "delFlag": false,
+                            "groupDelFlag": false,
+                            "groupId": 0,
+                            "groupBnName": "",
+                            "groupEnName": "",
+                            "sort": 0
+                          }
+                        ],
+                        "total": 0,
+                        "size": 0,
+                        "current": 0,
+                        "orders": [
+                          {
+                            "column": "",
+                            "asc": false
+                          }
+                        ],
+                        "optimizeCountSql": false,
+                        "searchCount": false,
+                        "optimizeJoinOfCountSql": false,
+                        "maxLimit": 0,
+                        "countId": ""
+                      }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/addQa": {
+      "post": {
+        "summary": "新增qa",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/QaRO",
+                "description": ""
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Result",
+                  "description": "统一API响应结果封装"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/updateQa/{id:^\\d+$}": {
+      "post": {
+        "summary": "修改qa",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "",
+            "required": true,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "requestBody": {
+          "content": {
+            "application/json": {
+              "schema": {
+                "$ref": "#/components/schemas/QaRO",
+                "description": ""
+              }
+            }
+          }
+        },
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Result",
+                  "description": "统一API响应结果封装"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/changeQaStatus/{id:^\\d+$}": {
+      "post": {
+        "summary": "修改qa状态",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "",
+            "required": true,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/Result",
+                  "description": "统一API响应结果封装"
+                },
+                "examples": {
+                  "1": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  },
+                  "2": {
+                    "summary": "成功示例",
+                    "value": {
+                      "code": "200",
+                      "message": "操作成功",
+                      "data": {}
+                    }
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/groupInfo/{id:^\\d+$}": {
+      "get": {
+        "summary": "分组详情",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "",
+            "required": true,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResultQaGroup"
+                },
+                "example": {
+                  "code": "",
+                  "message": "",
+                  "data": {
+                    "id": 0,
+                    "createUser": "",
+                    "updateUser": "",
+                    "updateTime": "",
+                    "createTime": "",
+                    "delFlag": false,
+                    "bnName": "",
+                    "enName": "",
+                    "sort": 0
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    },
+    "/qa/qaInfo/{id:^\\d+$}": {
+      "get": {
+        "summary": "qaInfo",
+        "deprecated": false,
+        "description": "",
+        "tags": [],
+        "parameters": [
+          {
+            "name": "id",
+            "in": "path",
+            "description": "",
+            "required": true,
+            "schema": {
+              "type": "integer"
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "",
+            "content": {
+              "application/json": {
+                "schema": {
+                  "$ref": "#/components/schemas/ResultQa"
+                },
+                "example": {
+                  "code": "",
+                  "message": "",
+                  "data": {
+                    "id": 0,
+                    "bnTitle": "",
+                    "enTitle": "",
+                    "bnContent": "",
+                    "enContent": "",
+                    "createUser": "",
+                    "updateUser": "",
+                    "updateTime": "",
+                    "createTime": "",
+                    "delFlag": false,
+                    "groupId": 0,
+                    "sort": 0
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "apikey-header-accesstoken": []
+          }
+        ]
+      }
+    }
+  },
+  "components": {
+    "schemas": {
+      "QaGroup": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "integer",
+            "description": "null",
+            "format": "int64"
+          },
+          "createUser": {
+            "type": "string",
+            "description": "创建人"
+          },
+          "updateUser": {
+            "type": "string",
+            "description": "更新人"
+          },
+          "updateTime": {
+            "type": "string",
+            "description": "更新时间"
+          },
+          "createTime": {
+            "type": "string",
+            "description": "创建时间"
+          },
+          "delFlag": {
+            "type": "boolean",
+            "description": "删除标识"
+          },
+          "bnName": {
+            "type": "string",
+            "description": "孟加拉分类名称"
+          },
+          "enName": {
+            "type": "string",
+            "description": "英文分类名称"
+          },
+          "sort": {
+            "type": "integer",
+            "description": "排序"
+          }
+        }
+      },
+      "OrderItem": {
+        "type": "object",
+        "properties": {
+          "column": {
+            "type": "string",
+            "description": ""
+          },
+          "asc": {
+            "type": "boolean",
+            "description": "",
+            "default": true
+          }
+        }
+      },
+      "PageQaGroup": {
+        "type": "object",
+        "properties": {
+          "records": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/QaGroup",
+              "description": ""
+            },
+            "description": "查询数据列表",
+            "default": "Collections.emptyList()"
+          },
+          "total": {
+            "type": "integer",
+            "description": "总数",
+            "format": "int64",
+            "default": 0
+          },
+          "size": {
+            "type": "integer",
+            "description": "每页显示条数,默认 10",
+            "format": "int64",
+            "default": 10
+          },
+          "current": {
+            "type": "integer",
+            "description": "当前页",
+            "format": "int64",
+            "default": 1
+          },
+          "orders": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/OrderItem",
+              "description": "com.baomidou.mybatisplus.core.metadata.OrderItem"
+            },
+            "description": "排序字段信息",
+            "default": "new ArrayList<>()"
+          },
+          "optimizeCountSql": {
+            "type": "boolean",
+            "description": "自动优化 COUNT SQL",
+            "default": true
+          },
+          "searchCount": {
+            "type": "boolean",
+            "description": "是否进行 count 查询",
+            "default": true
+          },
+          "optimizeJoinOfCountSql": {
+            "type": "boolean",
+            "description": "{@link #optimizeJoinOfCountSql()}",
+            "default": true
+          },
+          "maxLimit": {
+            "type": "integer",
+            "description": "单页分页条数限制",
+            "format": "int64"
+          },
+          "countId": {
+            "type": "string",
+            "description": "countId"
+          }
+        }
+      },
+      "ResultPageQaGroup": {
+        "type": "object",
+        "properties": {
+          "code": {
+            "type": "string",
+            "description": ""
+          },
+          "message": {
+            "type": "string",
+            "description": ""
+          },
+          "data": {
+            "$ref": "#/components/schemas/PageQaGroup",
+            "description": ""
+          }
+        }
+      },
+      "QaGroupParam": {
+        "type": "object",
+        "properties": {
+          "page": {
+            "type": "integer",
+            "description": "第几页"
+          },
+          "size": {
+            "type": "integer",
+            "description": "每页显示条数"
+          }
+        }
+      },
+      "QaGroupRO": {
+        "type": "object",
+        "properties": {
+          "bnName": {
+            "type": "string",
+            "description": "孟加拉分类名称"
+          },
+          "enName": {
+            "type": "string",
+            "description": "英文分类名称"
+          },
+          "sort": {
+            "type": "integer",
+            "description": "排序"
+          }
+        },
+        "required": [
+          "bnName",
+          "enName",
+          "sort"
+        ]
+      },
+      "ResultListQaGroup": {
+        "type": "object",
+        "properties": {
+          "code": {
+            "type": "string",
+            "description": ""
+          },
+          "message": {
+            "type": "string",
+            "description": ""
+          },
+          "data": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/QaGroup",
+              "description": ""
+            },
+            "description": ""
+          }
+        }
+      },
+      "ResultQaGroup": {
+        "type": "object",
+        "properties": {
+          "code": {
+            "type": "string",
+            "description": ""
+          },
+          "message": {
+            "type": "string",
+            "description": ""
+          },
+          "data": {
+            "$ref": "#/components/schemas/QaGroup",
+            "description": ""
+          }
+        }
+      },
+      "Result": {
+        "type": "object",
+        "properties": {
+          "code": {
+            "type": "string",
+            "description": ""
+          },
+          "message": {
+            "type": "string",
+            "description": ""
+          },
+          "data": {
+            "type": "object",
+            "properties": {},
+            "description": ""
+          }
+        }
+      },
+      "QaListVO": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "integer",
+            "description": "",
+            "format": "int64"
+          },
+          "bnTitle": {
+            "type": "string",
+            "description": "孟加拉标题"
+          },
+          "enTitle": {
+            "type": "string",
+            "description": "英文标题"
+          },
+          "createUser": {
+            "type": "string",
+            "description": "创建人"
+          },
+          "updateUser": {
+            "type": "string",
+            "description": "更新人"
+          },
+          "updateTime": {
+            "type": "string",
+            "description": "更新时间"
+          },
+          "createTime": {
+            "type": "string",
+            "description": "创建时间"
+          },
+          "delFlag": {
+            "type": "boolean",
+            "description": "删除标识"
+          },
+          "groupDelFlag": {
+            "type": "boolean",
+            "description": "分类删除标识"
+          },
+          "groupId": {
+            "type": "integer",
+            "description": "分类id",
+            "format": "int64"
+          },
+          "groupBnName": {
+            "type": "string",
+            "description": "分类名称"
+          },
+          "groupEnName": {
+            "type": "string",
+            "description": "分类名称"
+          },
+          "sort": {
+            "type": "integer",
+            "description": "排序"
+          }
+        }
+      },
+      "PageQaListVO": {
+        "type": "object",
+        "properties": {
+          "records": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/QaListVO",
+              "description": ""
+            },
+            "description": "查询数据列表",
+            "default": "Collections.emptyList()"
+          },
+          "total": {
+            "type": "integer",
+            "description": "总数",
+            "format": "int64",
+            "default": 0
+          },
+          "size": {
+            "type": "integer",
+            "description": "每页显示条数,默认 10",
+            "format": "int64",
+            "default": 10
+          },
+          "current": {
+            "type": "integer",
+            "description": "当前页",
+            "format": "int64",
+            "default": 1
+          },
+          "orders": {
+            "type": "array",
+            "items": {
+              "$ref": "#/components/schemas/OrderItem",
+              "description": "com.baomidou.mybatisplus.core.metadata.OrderItem"
+            },
+            "description": "排序字段信息",
+            "default": "new ArrayList<>()"
+          },
+          "optimizeCountSql": {
+            "type": "boolean",
+            "description": "自动优化 COUNT SQL",
+            "default": true
+          },
+          "searchCount": {
+            "type": "boolean",
+            "description": "是否进行 count 查询",
+            "default": true
+          },
+          "optimizeJoinOfCountSql": {
+            "type": "boolean",
+            "description": "{@link #optimizeJoinOfCountSql()}",
+            "default": true
+          },
+          "maxLimit": {
+            "type": "integer",
+            "description": "单页分页条数限制",
+            "format": "int64"
+          },
+          "countId": {
+            "type": "string",
+            "description": "countId"
+          }
+        }
+      },
+      "ResultPageQaListVO": {
+        "type": "object",
+        "properties": {
+          "code": {
+            "type": "string",
+            "description": ""
+          },
+          "message": {
+            "type": "string",
+            "description": ""
+          },
+          "data": {
+            "$ref": "#/components/schemas/PageQaListVO",
+            "description": ""
+          }
+        }
+      },
+      "QaRO": {
+        "type": "object",
+        "properties": {
+          "bnTitle": {
+            "type": "string",
+            "description": "孟加拉标题"
+          },
+          "enTitle": {
+            "type": "string",
+            "description": "英文标题"
+          },
+          "bnContent": {
+            "type": "string",
+            "description": "孟加拉内容"
+          },
+          "enContent": {
+            "type": "string",
+            "description": "英文内容"
+          },
+          "groupId": {
+            "type": "integer",
+            "description": "分类ID",
+            "format": "int64"
+          },
+          "delFlag": {
+            "type": "boolean",
+            "description": "删除标识"
+          },
+          "sort": {
+            "type": "integer",
+            "description": "排序"
+          }
+        },
+        "required": [
+          "bnTitle",
+          "enTitle",
+          "bnContent",
+          "enContent",
+          "groupId",
+          "delFlag",
+          "sort"
+        ]
+      },
+      "Qa": {
+        "type": "object",
+        "properties": {
+          "id": {
+            "type": "integer",
+            "description": "null",
+            "format": "int64"
+          },
+          "bnTitle": {
+            "type": "string",
+            "description": "孟加拉标题"
+          },
+          "enTitle": {
+            "type": "string",
+            "description": "英文标题"
+          },
+          "bnContent": {
+            "type": "string",
+            "description": "孟加拉内容"
+          },
+          "enContent": {
+            "type": "string",
+            "description": "英文内容"
+          },
+          "createUser": {
+            "type": "string",
+            "description": "创建人"
+          },
+          "updateUser": {
+            "type": "string",
+            "description": "更新人"
+          },
+          "updateTime": {
+            "type": "string",
+            "description": "更新时间"
+          },
+          "createTime": {
+            "type": "string",
+            "description": "创建时间"
+          },
+          "delFlag": {
+            "type": "boolean",
+            "description": "删除标识"
+          },
+          "groupId": {
+            "type": "integer",
+            "description": "分类id",
+            "format": "int64"
+          },
+          "sort": {
+            "type": "integer",
+            "description": "排序"
+          }
+        }
+      },
+      "ResultQa": {
+        "type": "object",
+        "properties": {
+          "code": {
+            "type": "string",
+            "description": ""
+          },
+          "message": {
+            "type": "string",
+            "description": ""
+          },
+          "data": {
+            "$ref": "#/components/schemas/Qa",
+            "description": ""
+          }
+        }
+      }
+    },
+    "responses": {},
+    "securitySchemes": {
+      "apikey-header-accesstoken": {
+        "type": "apiKey",
+        "in": "header",
+        "name": "accesstoken"
+      }
+    }
+  },
+  "servers": [],
+  "security": []
+}

+ 108 - 0
src/app/shop/admin/content/content.service.js

@@ -34,6 +34,22 @@ const route = {
         title: '短信',
       },
     },
+    {
+      path: 'qa-category',
+      name: 'shop.admin.content.qa-category',
+      component: () => import('./qa/category/index.vue'),
+      meta: {
+        title: '问答分类',
+      },
+    },
+    {
+      path: 'qa',
+      name: 'shop.admin.content.qa',
+      component: () => import('./qa/list/index.vue'),
+      meta: {
+        title: '问答管理',
+      },
+    },
   ],
 };
 
@@ -92,6 +108,98 @@ const api = {
     ...CRUD('shop/admin/sms_template'),
     select: (params) => SELECT('shop/admin/sms_template', params),
   },
+
+  // 问答分类相关 API
+  qaGroup: {
+    // 分类列表(分页)
+    list: (params) =>
+      request({
+        url: 'cif/qa/groupList',
+        method: 'GET',
+        params,
+      }),
+    // 可用分类列表
+    availableList: () =>
+      request({
+        url: 'cif/qa/availableGroupList',
+        method: 'GET',
+      }),
+    // 分类详情
+    detail: (id) =>
+      request({
+        url: `cif/qa/groupInfo/${id}`,
+        method: 'GET',
+      }),
+    // 新增分类
+    add: (data) =>
+      request({
+        url: 'cif/qa/add',
+        method: 'POST',
+        data,
+      }),
+    // 修改分类
+    edit: (id, data) =>
+      request({
+        url: `cif/qa/update/${id}`,
+        method: 'POST',
+        data,
+      }),
+    // 禁用分类
+    disable: (id) =>
+      request({
+        url: `cif/qa/disable/${id}`,
+        method: 'POST',
+      }),
+    // 修改分类状态
+    changeStatus: (id) =>
+      request({
+        url: `cif/qa/changeStatus/${id}`,
+        method: 'POST',
+      }),
+  },
+
+  // 问答相关 API
+  qa: {
+    // 问答列表(分页)
+    list: (params) =>
+      request({
+        url: 'cif/qa/qaList',
+        method: 'GET',
+        params,
+      }),
+    // 问答详情
+    detail: (id) =>
+      request({
+        url: `cif/qa/qaInfo/${id}`,
+        method: 'GET',
+      }),
+    // 新增问答
+    add: (data) =>
+      request({
+        url: 'cif/qa/addQa',
+        method: 'POST',
+        data,
+      }),
+    // 修改问答
+    edit: (id, data) =>
+      request({
+        url: `cif/qa/updateQa/${id}`,
+        method: 'POST',
+        data,
+      }),
+    // 修改问答状态
+    changeStatus: (id) =>
+      request({
+        url: `cif/qa/changeQaStatus/${id}`,
+        method: 'POST',
+      }),
+    // 删除问答
+    delete: (id) =>
+      request({
+        url: `cif/qa/deleteQa/${id}`,
+        method: 'POST',
+      }),
+  },
 };
 
 export { route, api };

+ 93 - 0
src/app/shop/admin/content/qa/category/edit.vue

@@ -0,0 +1,93 @@
+<template>
+    <el-container>
+        <el-main>
+            <el-form :model="form.model" :rules="form.rules" ref="formRef" :label-width="formLabelWidth">
+                <el-form-item :label="t('modules.qa.bnName')" prop="bnName">
+                    <el-input v-model="form.model.bnName" :placeholder="t('modules.qa.enterBnName')"></el-input>
+                </el-form-item>
+                <el-form-item :label="t('modules.qa.enName')" prop="enName">
+                    <el-input v-model="form.model.enName" :placeholder="t('modules.qa.enterEnName')"></el-input>
+                </el-form-item>
+                <el-form-item :label="t('form.sort')" prop="sort">
+                    <el-input v-model="form.model.sort" :placeholder="t('form.inputSort')"></el-input>
+                </el-form-item>
+            </el-form>
+        </el-main>
+        <el-footer class="sa-footer--submit">
+            <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">{{
+                t('common.save')
+                }}</el-button>
+            <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">{{
+                t('common.update')
+                }}</el-button>
+        </el-footer>
+    </el-container>
+</template>
+<script setup>
+import { cloneDeep } from 'lodash';
+import { onMounted, reactive, ref, unref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { ElMessage } from 'element-plus';
+import { useFormConfig } from '@/hooks/useFormConfig';
+import { api } from '../../content.service';
+
+const { t } = useI18n();
+
+// 使用表单配置hooks
+const { formLabelWidth } = useFormConfig({ enWidth: '140px' });
+
+const emit = defineEmits(['modalCallBack']);
+const props = defineProps({
+    modal: {
+        type: Object,
+    },
+});
+// 添加 编辑 form
+let formRef = ref(null);
+const form = reactive({
+    model: {
+        bnName: '',
+        enName: '',
+        sort: '0',
+    },
+    get rules() {
+        return {
+            bnName: [{ required: true, message: t('modules.qa.bnNameRequired'), trigger: 'blur' }],
+            enName: [{ required: true, message: t('modules.qa.enNameRequired'), trigger: 'blur' }],
+        };
+    },
+});
+const loading = ref(false);
+// 获取详情
+async function getDetail(id) {
+    loading.value = true;
+    const { code, data } = await api.qaGroup.detail(id);
+    if (code == 200 || code == '200') {
+        form.model = data;
+    }
+    loading.value = false;
+}
+// 表单关闭时提交
+async function confirm() {
+    unref(formRef).validate(async (valid) => {
+        if (!valid) return;
+        let submitForm = cloneDeep(form.model);
+        const { code } =
+            props.modal.params.type == 'add'
+                ? await api.qaGroup.add(submitForm)
+                : await api.qaGroup.edit(props.modal.params.id, submitForm);
+        if (code == '200' || code == 200) {
+            ElMessage.success(t('message.saveSuccess'));
+            emit('modalCallBack', { event: 'confirm' });
+        }
+    });
+}
+async function init() {
+    if (props.modal.params.id) {
+        await getDetail(props.modal.params.id);
+    }
+}
+onMounted(() => {
+    init();
+});
+</script>

+ 164 - 0
src/app/shop/admin/content/qa/category/index.vue

@@ -0,0 +1,164 @@
+<template>
+    <el-container class="qa-category-view panel-block">
+        <el-header class="sa-header">
+            <div class="sa-title sa-flex sa-row-between">
+                <div class="label sa-flex">{{ t('modules.qa.categoryManagement') }}</div>
+                <div>
+                    <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
+                    <el-button icon="Plus" type="primary" @click="addRow">{{ t('common.create') }}</el-button>
+                </div>
+            </div>
+        </el-header>
+        <el-main class="sa-p-0">
+            <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
+                <el-table height="100%" class="sa-table" :data="table.data" row-key="id" stripe>
+                    <template #empty>
+                        <sa-empty />
+                    </template>
+                    <el-table-column prop="id" label="ID" min-width="100">
+                    </el-table-column>
+                    <el-table-column :label="t('modules.qa.bnName')" min-width="150">
+                        <template #default="scope">
+                            <span class="sa-table-line-1">
+                                {{ scope.row.bnName || '-' }}
+                            </span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('modules.qa.enName')" min-width="150">
+                        <template #default="scope">
+                            <span class="sa-table-line-1">
+                                {{ scope.row.enName || '-' }}
+                            </span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('form.sort')" min-width="80" align="center">
+                        <template #default="scope">
+                            {{ scope.row.sort || 0 }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('form.status')" min-width="100" align="center">
+                        <template #default="scope">
+                            <el-tag :type="scope.row.delFlag ? 'danger' : 'success'">
+                                {{ scope.row.delFlag ? t('modules.qa.disabled') : t('modules.qa.enabled') }}
+                            </el-tag>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('form.createTime')" min-width="160">
+                        <template #default="scope">
+                            {{ scope.row.createTime || '-' }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('form.updateTime')">
+                        <template #default="scope">
+                            {{ scope.row.updateTime || '-' }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column fixed="right" :label="t('common.actions')">
+                        <template #default="scope">
+                            <el-button class="is-link" type="primary" @click="editRow(scope.row)">{{
+                                t('common.edit')
+                            }}</el-button>
+                            <el-button class="is-link" :type="scope.row.delFlag ? 'success' : 'warning'"
+                                @click="toggleStatus(scope.row)">
+                                {{ scope.row.delFlag ? t('modules.qa.enable') : t('modules.qa.disable') }}
+                            </el-button>
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </div>
+        </el-main>
+        <sa-view-bar>
+            <template #right>
+                <sa-pagination :pageData="pageData" @updateFn="getData" />
+            </template>
+        </sa-view-bar>
+    </el-container>
+</template>
+<script setup>
+import { onMounted, reactive, ref } from 'vue';
+import { api } from '../../content.service';
+import { ElMessage } from 'element-plus';
+import { useModal } from '@/sheep/hooks';
+import { usePagination } from '@/sheep/hooks';
+import { useI18n } from 'vue-i18n';
+import categoryEdit from './edit.vue';
+
+const { t } = useI18n();
+const { pageData } = usePagination();
+
+// 列表
+const table = reactive({
+    data: [],
+});
+const loading = ref(true);
+
+// 获取数据
+async function getData(page) {
+    if (page) pageData.page = page;
+    loading.value = true;
+
+    const { code, data } = await api.qaGroup.list({
+        page: pageData.page,
+        size: pageData.size,
+    });
+    if (code == 200 || code == '200') {
+        table.data = data.list || [];
+        pageData.total = data.total;
+    }
+    loading.value = false;
+}
+
+function addRow() {
+    useModal(
+        categoryEdit,
+        { title: t('modules.qa.addCategory'), type: 'add' },
+        {
+            confirm: () => {
+                getData();
+            },
+        },
+    );
+}
+
+function editRow(row) {
+    useModal(
+        categoryEdit,
+        {
+            title: t('modules.qa.editCategory'),
+            type: 'edit',
+            id: row.id,
+        },
+        {
+            confirm: () => {
+                getData();
+            },
+        },
+    );
+}
+
+// 切换状态
+async function toggleStatus(row) {
+    const { code } = await api.qaGroup.changeStatus(row.id);
+    if (code == 200 || code == '200') {
+        ElMessage.success(t('message.operationSuccess'));
+        getData();
+    }
+}
+
+onMounted(() => {
+    getData();
+});
+</script>
+<style lang="scss" scoped>
+.qa-category-view {
+    .el-header {
+        height: auto;
+    }
+
+    .el-main {
+        .sa-table-wrap {
+            height: 100%;
+        }
+    }
+}
+</style>

+ 124 - 0
src/app/shop/admin/content/qa/list/edit.vue

@@ -0,0 +1,124 @@
+<template>
+    <el-container class="qa-edit-container">
+        <el-main>
+            <el-form :model="form.model" :rules="form.rules" ref="formRef" :label-width="formLabelWidth">
+                <el-form-item :label="t('modules.qa.category')" prop="groupId">
+                    <el-select v-model="form.model.groupId" :placeholder="t('modules.qa.selectCategory')"
+                        style="width: 100%">
+                        <el-option v-for="item in categoryList" :key="item.id"
+                            :label="`${item.bnName || ''} / ${item.enName || ''}`" :value="item.id" />
+                    </el-select>
+                </el-form-item>
+                <el-form-item :label="t('modules.qa.bnTitle')" prop="bnTitle">
+                    <el-input v-model="form.model.bnTitle" :placeholder="t('modules.qa.enterBnTitle')"></el-input>
+                </el-form-item>
+                <el-form-item :label="t('modules.qa.enTitle')" prop="enTitle">
+                    <el-input v-model="form.model.enTitle" :placeholder="t('modules.qa.enterEnTitle')"></el-input>
+                </el-form-item>
+                <el-form-item :label="t('modules.qa.bnContent')" prop="bnContent">
+                    <sa-editor v-model:content="form.model.bnContent" :direct-upload="true" />
+                </el-form-item>
+                <el-form-item :label="t('modules.qa.enContent')" prop="enContent">
+                    <sa-editor v-model:content="form.model.enContent" :direct-upload="true" />
+                </el-form-item>
+                <el-form-item :label="t('form.sort')" prop="sort">
+                    <el-input v-model="form.model.sort" :placeholder="t('form.inputSort')"></el-input>
+                </el-form-item>
+            </el-form>
+        </el-main>
+        <el-footer class="sa-footer--submit">
+            <el-button v-if="modal.params.type == 'add'" type="primary" @click="confirm">{{
+                t('common.save')
+            }}</el-button>
+            <el-button v-if="modal.params.type == 'edit'" type="primary" @click="confirm">{{
+                t('common.update')
+            }}</el-button>
+        </el-footer>
+    </el-container>
+</template>
+<script setup>
+import { cloneDeep } from 'lodash';
+import { onMounted, reactive, ref, unref, computed } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { ElMessage } from 'element-plus';
+import { useFormConfig } from '@/hooks/useFormConfig';
+import { api } from '../../content.service';
+import saEditor from '@/sheep/components/sa-editor/sa-editor.vue';
+
+const { t } = useI18n();
+
+// 使用表单配置hooks
+const { formLabelWidth } = useFormConfig({ enWidth: '140px' });
+
+const emit = defineEmits(['modalCallBack']);
+const props = defineProps({
+    modal: {
+        type: Object,
+    },
+});
+
+// 分类列表
+const categoryList = computed(() => props.modal.params.categoryList || []);
+
+// 添加 编辑 form
+let formRef = ref(null);
+const form = reactive({
+    model: {
+        groupId: '',
+        bnTitle: '',
+        enTitle: '',
+        bnContent: '',
+        enContent: '',
+        sort: '0',
+        delFlag: false,
+    },
+    get rules() {
+        return {
+            groupId: [{ required: true, message: t('modules.qa.categoryRequired'), trigger: 'change' }],
+            bnTitle: [{ required: true, message: t('modules.qa.bnTitleRequired'), trigger: 'blur' }],
+            enTitle: [{ required: true, message: t('modules.qa.enTitleRequired'), trigger: 'blur' }],
+        };
+    },
+});
+const loading = ref(false);
+// 获取详情
+async function getDetail(id) {
+    loading.value = true;
+    const { code, data } = await api.qa.detail(id);
+    if (code == 200 || code == '200') {
+        form.model = data;
+    }
+    loading.value = false;
+}
+// 表单关闭时提交
+async function confirm() {
+    unref(formRef).validate(async (valid) => {
+        if (!valid) return;
+        let submitForm = cloneDeep(form.model);
+        const { code } =
+            props.modal.params.type == 'add'
+                ? await api.qa.add(submitForm)
+                : await api.qa.edit(props.modal.params.id, submitForm);
+        if (code == '200' || code == 200) {
+            ElMessage.success(t('message.saveSuccess'));
+            emit('modalCallBack', { event: 'confirm' });
+        }
+    });
+}
+async function init() {
+    if (props.modal.params.id) {
+        await getDetail(props.modal.params.id);
+    }
+}
+onMounted(() => {
+    init();
+});
+</script>
+<style lang="scss" scoped>
+.qa-edit-container {
+    .el-main {
+        max-height: 70vh;
+        overflow-y: auto;
+    }
+}
+</style>

+ 240 - 0
src/app/shop/admin/content/qa/list/index.vue

@@ -0,0 +1,240 @@
+<template>
+    <el-container class="qa-list-view panel-block">
+        <el-header class="sa-header">
+            <!-- 简化搜索组件 -->
+            <div class="search-container">
+                <sa-search-simple :searchFields="searchFields" :defaultValues="defaultSearchValues"
+                    v-model="currentSearchParams" @search="handleSearch" @reset="handleReset">
+                </sa-search-simple>
+            </div>
+            <div class="sa-title sa-flex sa-row-between">
+                <div class="label sa-flex">{{ t('modules.qa.qaManagement') }}</div>
+                <div>
+                    <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
+                    <el-button icon="Plus" type="primary" @click="addRow">{{ t('common.create') }}</el-button>
+                </div>
+            </div>
+        </el-header>
+        <el-main class="sa-p-0">
+            <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
+                <el-table height="100%" class="sa-table" :data="table.data" row-key="id" stripe>
+                    <template #empty>
+                        <sa-empty />
+                    </template>
+                    <el-table-column prop="id" label="ID" min-width="100">
+                    </el-table-column>
+                    <el-table-column :label="t('modules.qa.bnTitle')" min-width="180">
+                        <template #default="scope">
+                            <span class="sa-table-line-2">
+                                {{ scope.row.bnTitle || '-' }}
+                            </span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('modules.qa.enTitle')" min-width="180">
+                        <template #default="scope">
+                            <span class="sa-table-line-2">
+                                {{ scope.row.enTitle || '-' }}
+                            </span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('modules.qa.category')" min-width="120">
+                        <template #default="scope">
+                            <span>{{ `${scope.row.groupBnName} / ${scope.row.groupEnName}` || '-' }}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('form.sort')" min-width="80" align="center">
+                        <template #default="scope">
+                            {{ scope.row.sort || 0 }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('form.status')" min-width="100" align="center">
+                        <template #default="scope">
+                            <el-tag :type="scope.row.delFlag ? 'danger' : 'success'">
+                                {{ scope.row.delFlag ? t('modules.qa.disabled') : t('modules.qa.enabled') }}
+                            </el-tag>
+                        </template>
+                    </el-table-column>
+                    <el-table-column :label="t('form.createTime')" min-width="160">
+                        <template #default="scope">
+                            {{ scope.row.createTime || '-' }}
+                        </template>
+                    </el-table-column>
+                    <el-table-column fixed="right" :label="t('common.actions')">
+                        <template #default="scope">
+                            <el-button class="is-link" type="primary" @click="editRow(scope.row)">{{ t('common.edit')
+                                }}</el-button>
+                            <el-button class="is-link" :type="scope.row.delFlag ? 'success' : 'warning'"
+                                @click="toggleStatus(scope.row)">
+                                {{ scope.row.delFlag ? t('modules.qa.enable') : t('modules.qa.disable') }}
+                            </el-button>
+                            <!-- <el-button class="is-link" type="danger"
+                                @click="deleteRow(scope.row)">{{ t('common.delete') }}</el-button> -->
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </div>
+        </el-main>
+        <sa-view-bar>
+            <template #right>
+                <sa-pagination :pageData="pageData" @updateFn="getData" />
+            </template>
+        </sa-view-bar>
+    </el-container>
+</template>
+<script setup>
+import { onMounted, reactive, ref } from 'vue';
+import { api } from '../../content.service';
+import { ElMessage } from 'element-plus';
+import { useModal } from '@/sheep/hooks';
+import { usePagination } from '@/sheep/hooks';
+import { useI18n } from 'vue-i18n';
+import qaEdit from './edit.vue';
+
+const { t } = useI18n();
+const { pageData } = usePagination();
+
+// 分类列表
+const categoryList = ref([]);
+
+// 搜索字段配置
+const searchFields = reactive({
+    groupId: {
+        type: 'select',
+        get label() {
+            return t('modules.qa.category');
+        },
+        get placeholder() {
+            return t('modules.qa.selectCategory');
+        },
+        width: 200,
+        get options() {
+            return categoryList.value.map(item => ({
+                label: item.bnName || item.enName,
+                value: item.id,
+            }));
+        },
+    },
+});
+
+// 默认搜索值
+const defaultSearchValues = reactive({
+    groupId: '',
+});
+
+// 当前搜索条件 - 使用 ref 支持双向绑定
+const currentSearchParams = ref({});
+
+// 列表
+const table = reactive({
+    data: [],
+});
+const loading = ref(true);
+
+// 获取分类列表
+async function getCategoryList() {
+    const { code, data } = await api.qaGroup.availableList();
+    if (code == 200 || code == '200') {
+        categoryList.value = data || [];
+    }
+}
+
+// 获取数据
+async function getData(page, searchParams = null) {
+    if (page) pageData.page = page;
+    loading.value = true;
+
+    // 构建请求参数 - 优先使用传入的参数,否则使用双向绑定的搜索条件
+    const finalSearchParams = searchParams !== null ? searchParams : currentSearchParams.value;
+
+    const { code, data } = await api.qa.list({
+        page: pageData.page,
+        size: pageData.size,
+        groupId: finalSearchParams.groupId || undefined,
+    });
+    if (code == 200 || code == '200') {
+        table.data = data.list || [];
+        pageData.total = data.total;
+    }
+    loading.value = false;
+}
+
+// 搜索处理函数
+function handleSearch() {
+    getData(1);
+}
+
+// 重置搜索参数处理函数
+function handleReset() {
+    currentSearchParams.value = {};
+    getData(1, {});
+}
+
+function addRow() {
+    useModal(
+        qaEdit,
+        {
+            title: t('modules.qa.addQa'),
+            type: 'add',
+            categoryList: categoryList.value,
+        },
+        {
+            confirm: () => {
+                getData();
+            },
+        },
+    );
+}
+
+function editRow(row) {
+    useModal(
+        qaEdit,
+        {
+            title: t('modules.qa.editQa'),
+            type: 'edit',
+            id: row.id,
+            categoryList: categoryList.value,
+        },
+        {
+            confirm: () => {
+                getData();
+            },
+        },
+    );
+}
+
+// 切换状态
+async function toggleStatus(row) {
+    const { code } = await api.qa.changeStatus(row.id);
+    if (code == 200 || code == '200') {
+        ElMessage.success(t('message.operationSuccess'));
+        getData();
+    }
+}
+
+// 删除
+async function deleteRow(row) {
+    const { code } = await api.qa.edit(row.id, { ...row, delFlag: true });
+    if (code == 200 || code == '200') {
+        ElMessage.success(t('message.deleteSuccess'));
+        getData();
+    }
+}
+
+onMounted(async () => {
+    await getCategoryList();
+    getData();
+});
+</script>
+<style lang="scss" scoped>
+.qa-list-view {
+    .el-header {
+        height: auto;
+    }
+
+    .el-main {
+        .sa-table-wrap {
+            height: 100%;
+        }
+    }
+}
+</style>

+ 29 - 4
src/assets/css/sa-reset.scss

@@ -20,9 +20,6 @@ h3,
 h4,
 h5,
 h6,
-em,
-i,
-b,
 textarea,
 button,
 input,
@@ -32,7 +29,6 @@ figcaption {
   padding: 0;
   margin: 0;
   list-style-type: none;
-  font-style: normal;
   text-decoration: none;
   border: none;
   background: none;
@@ -94,3 +90,32 @@ a:active {
 .el-button:focus {
   outline: none !important;
 }
+
+// 富文本编辑器样式覆盖
+.w-e-text-container [data-slate-editor] {
+  strong,
+  strong *,
+  b,
+  b * {
+    font-weight: bold !important;
+  }
+
+  em,
+  em *,
+  i,
+  i * {
+    font-style: italic !important;
+  }
+
+  u,
+  u * {
+    text-decoration: underline !important;
+  }
+
+  s,
+  s *,
+  strike,
+  strike * {
+    text-decoration: line-through !important;
+  }
+}

+ 37 - 1
src/locales/en-US/index.json

@@ -274,6 +274,8 @@
     "messagePush": "Push",
     "sms": "SMS",
     "bannerAd": "Banner",
+    "qaCategory": "Q&A Cat",
+    "qaManagement": "Q&A",
     "data": "Data",
     "dataReport": "Data Reports",
     "finance": "Finance",
@@ -433,6 +435,7 @@
     "updateFailed": "Update failed",
     "createFailed": "Create failed",
     "uploadFailed": "{count} images failed to upload",
+    "imageUploadFailed": "Image upload failed",
     "networkError": "Network error",
     "serverError": "Server error",
     "unsavedChanges": "You have unsaved changes",
@@ -478,7 +481,9 @@
     "passwordChangeSuccess": "Password changed successfully, please login again",
     "editFeatureNotDeveloped": "Edit feature is under development",
     "goodsAttributeSaveSuccess": "Product attributes saved successfully",
-    "invalidAspectRatio": "Image aspect ratio does not meet requirements, please upload {ratio} ratio image"
+    "invalidAspectRatio": "Image aspect ratio does not meet requirements, please upload {ratio} ratio image",
+    "operationSuccess": "Operation successful",
+    "operationFailed": "Operation failed"
   },
   "placeholders": {
     "inputOrderNo": "Please enter order number",
@@ -1658,6 +1663,37 @@
       "countdownTimeRequired": "Please select countdown time",
       "goodsConflictError": "Goods with ID {ids} already exist in other activities, failed to add",
       "setGoodsFailed": "Failed to set goods"
+    },
+    "qa": {
+      "categoryManagement": "Q&A Category Management",
+      "qaManagement": "Q&A Management",
+      "addCategory": "Add Category",
+      "editCategory": "Edit Category",
+      "addQa": "Add Q&A",
+      "editQa": "Edit Q&A",
+      "bnName": "Bengali Name",
+      "enName": "English Name",
+      "bnTitle": "Bengali Title",
+      "enTitle": "English Title",
+      "bnContent": "Bengali Content",
+      "enContent": "English Content",
+      "category": "Category",
+      "enterBnName": "Please enter Bengali name",
+      "enterEnName": "Please enter English name",
+      "enterBnTitle": "Please enter Bengali title",
+      "enterEnTitle": "Please enter English title",
+      "selectCategory": "Please select category",
+      "enabled": "Enabled",
+      "disabled": "Disabled",
+      "enable": "Enable",
+      "disable": "Disable",
+      "bnNameRequired": "Please enter Bengali name",
+      "enNameRequired": "Please enter English name",
+      "bnTitleRequired": "Please enter Bengali title",
+      "enTitleRequired": "Please enter English title",
+      "bnContentRequired": "Please enter Bengali content",
+      "enContentRequired": "Please enter English content",
+      "categoryRequired": "Please select category"
     }
   }
 }

+ 2 - 0
src/locales/navigation.js

@@ -37,6 +37,8 @@ export const navigationMap = {
   'shop.admin.content.notification': 'menu.messagePush',
   'shop.admin.content.sms': 'menu.sms',
   'shop.admin.content.adv': 'menu.bannerAd',
+  'shop.admin.content.qa-category': 'menu.qaCategory',
+  'shop.admin.content.qa': 'menu.qaManagement',
 
   // 三级菜单 - 数据
   'shop.admin.data.report': 'menu.dataReport',

+ 37 - 1
src/locales/zh-CN/index.json

@@ -274,6 +274,8 @@
     "messagePush": "消息推送",
     "sms": "短信",
     "bannerAd": "广告位",
+    "qaCategory": "问答分类",
+    "qaManagement": "问答管理",
     "data": "数据",
     "dataReport": "数据报表",
     "finance": "财务",
@@ -431,6 +433,7 @@
     "updateFailed": "更新失败",
     "createFailed": "创建失败",
     "uploadFailed": "{count}张图片上传失败",
+    "imageUploadFailed": "图片上传失败",
     "networkError": "网络错误",
     "serverError": "服务器错误",
     "unsavedChanges": "有未保存的更改",
@@ -477,7 +480,9 @@
     "passwordChangeSuccess": "密码修改成功,请重新登录",
     "editFeatureNotDeveloped": "编辑功能待开发",
     "goodsAttributeSaveSuccess": "商品属性保存成功",
-    "invalidAspectRatio": "图片比例不符合要求,请上传{ratio}比例的图片"
+    "invalidAspectRatio": "图片比例不符合要求,请上传{ratio}比例的图片",
+    "operationSuccess": "操作成功",
+    "operationFailed": "操作失败"
   },
   "placeholders": {
     "inputOrderNo": "请输入订单号",
@@ -1671,6 +1676,37 @@
       "countdownTimeRequired": "请选择开团倒计时结束",
       "goodsConflictError": "商品ID为 {ids} 的商品已存在于其他活动中,添加失败",
       "setGoodsFailed": "设置商品失败"
+    },
+    "qa": {
+      "categoryManagement": "问答分类管理",
+      "qaManagement": "问答管理",
+      "addCategory": "新增分类",
+      "editCategory": "编辑分类",
+      "addQa": "新增问答",
+      "editQa": "编辑问答",
+      "bnName": "孟加拉语名称",
+      "enName": "英文名称",
+      "bnTitle": "孟加拉语标题",
+      "enTitle": "英文标题",
+      "bnContent": "孟加拉语内容",
+      "enContent": "英文内容",
+      "category": "所属分类",
+      "enterBnName": "请输入孟加拉语名称",
+      "enterEnName": "请输入英文名称",
+      "enterBnTitle": "请输入孟加拉语标题",
+      "enterEnTitle": "请输入英文标题",
+      "selectCategory": "请选择分类",
+      "enabled": "启用",
+      "disabled": "禁用",
+      "enable": "启用",
+      "disable": "禁用",
+      "bnNameRequired": "请输入孟加拉语名称",
+      "enNameRequired": "请输入英文名称",
+      "bnTitleRequired": "请输入孟加拉语标题",
+      "enTitleRequired": "请输入英文标题",
+      "bnContentRequired": "请输入孟加拉语内容",
+      "enContentRequired": "请输入英文内容",
+      "categoryRequired": "请选择分类"
     }
   }
 }

+ 145 - 79
src/sheep/components/sa-editor/sa-editor.vue

@@ -1,116 +1,182 @@
 <template>
-  <div class="sa-editor">
-    <Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
-    <Editor
-      style="height: 400px; overflow-y: hidden"
-      :defaultConfig="editorConfig"
-      :mode="mode"
-      v-model="valueHtml"
-      @onCreated="handleCreated"
-      @onChange="handleChange"
-    />
-  </div>
+    <div class="sa-editor">
+        <Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
+        <Editor style="height: 400px; overflow-y: hidden" :defaultConfig="editorConfig" :mode="mode" v-model="valueHtml"
+            @onCreated="handleCreated" @onChange="handleChange" />
+    </div>
 </template>
 
 <script setup>
-  import '@wangeditor/editor/dist/css/style.css';
-  import { onBeforeUnmount, ref, shallowRef, onMounted, watch, nextTick } from 'vue';
-  import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
-  import { useFile } from '@/sheep/hooks';
-  import { checkUrl } from '@/sheep/utils/checkUrlSuffix';
+import '@wangeditor/editor/dist/css/style.css';
+import { onBeforeUnmount, ref, shallowRef, onMounted, watch, nextTick } from 'vue';
+import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
+import { i18nChangeLanguage } from '@wangeditor/editor';
+import { useFile } from '@/sheep/hooks';
+import { checkUrl } from '@/sheep/utils/checkUrlSuffix';
+import adminApi from '@/app/admin/api';
+import { ElMessage } from 'element-plus';
+import { useI18n } from 'vue-i18n';
 
-  const emits = defineEmits(['update:content']);
-  const props = defineProps(['content']);
+const { t, locale } = useI18n();
 
-  const mode = 'default';
+// 根据当前语言设置编辑器语言
+const editorLang = locale.value === 'zh-CN' ? 'zh-CN' : 'en';
+i18nChangeLanguage(editorLang);
 
-  // 编辑器实例,必须用 shallowRef
-  const editorRef = shallowRef();
+const emits = defineEmits(['update:content']);
+const props = defineProps({
+    content: {
+        type: String,
+        default: '',
+    },
+    directUpload: {
+        type: Boolean,
+        default: false,
+    },
+});
+
+const mode = 'default';
 
-  // 内容 HTML
-  const valueHtml = ref(props.content);
-  watch(
+// 编辑器实例,必须用 shallowRef
+const editorRef = shallowRef();
+
+// 内容 HTML
+const valueHtml = ref(props.content);
+watch(
     () => props.content,
     () => {
-      valueHtml.value = props.content;
-      nextTick(() => {
-        editorRef.value.focus();
-      });
+        valueHtml.value = props.content;
+        nextTick(() => {
+            editorRef.value.focus();
+        });
     },
-  );
+);
 
-  const toolbarConfig = {};
+const toolbarConfig = {};
 
-  const editorConfig = { MENU_CONF: {} };
-  editorConfig.MENU_CONF['uploadImage'] = {
-    // 自定义选择图片
-    customBrowseAndUpload(insertFn) {
-      useFile(
-        {
-          fileType: 'image',
-          multiple: true,
+const editorConfig = { MENU_CONF: {} };
+
+// 根据 directUpload 参数选择上传方式
+if (props.directUpload) {
+    // 直接上传模式
+    editorConfig.MENU_CONF['uploadImage'] = {
+        // 自定义上传
+        async customUpload(file, insertFn) {
+            try {
+                const formData = new FormData();
+                formData.append('file', file);
+                const response = await adminApi.file.upload({}, formData);
+                if (response.code == '200') {
+                    insertFn(checkUrl(response.data), '', '');
+                } else {
+                    ElMessage.error(response.msg || t('message.imageUploadFailed'));
+                }
+            } catch (error) {
+                console.error('Upload image failed:', error);
+                ElMessage.error(t('message.imageUploadFailed'));
+            }
         },
-        {
-          confirm: (data) => {
-            data.forEach((item) => {
-              insertFn(checkUrl(item.url), '', ''); // url alt href
-            });
-          },
+    };
+} else {
+    // 文件管理器模式
+    editorConfig.MENU_CONF['uploadImage'] = {
+        // 自定义选择图片
+        customBrowseAndUpload(insertFn) {
+            useFile(
+                {
+                    fileType: 'image',
+                    multiple: true,
+                },
+                {
+                    confirm: (data) => {
+                        data.forEach((item) => {
+                            insertFn(checkUrl(item.url), '', ''); // url alt href
+                        });
+                    },
+                },
+            );
         },
-      );
-    },
-  };
-  editorConfig.MENU_CONF['uploadVideo'] = {
+    };
+}
+
+editorConfig.MENU_CONF['uploadVideo'] = {
     // 自定义选择视频
     customBrowseAndUpload(insertFn) {
-      useFile(
-        {
-          fileType: 'video',
-          multiple: true,
-        },
-        {
-          confirm: (data) => {
-            data.forEach((item) => {
-              insertFn(checkUrl(item.url), ''); // url poster
-            });
-          },
-        },
-      );
+        useFile(
+            {
+                fileType: 'video',
+                multiple: true,
+            },
+            {
+                confirm: (data) => {
+                    data.forEach((item) => {
+                        insertFn(checkUrl(item.url), ''); // url poster
+                    });
+                },
+            },
+        );
     },
-  };
-  editorConfig.MENU_CONF['lineHeight'] = {
+};
+editorConfig.MENU_CONF['lineHeight'] = {
     lineHeightList: ['0', '1', '1.5', '2', '2.5'],
-  };
+};
 
-  // 编辑器回调函数
-  const handleCreated = (editor) => {
+// 编辑器回调函数
+const handleCreated = (editor) => {
     editorRef.value = editor; // 记录 editor 实例
-  };
-  const handleChange = (editor) => {
+};
+const handleChange = (editor) => {
     emits('update:content', editor.getHtml());
-  };
+};
 
-  onMounted(() => {
+onMounted(() => {
     nextTick(() => {
-      editorRef.value.focus();
+        editorRef.value.focus();
     });
-  });
+});
 
-  onBeforeUnmount(() => {
+onBeforeUnmount(() => {
     const editor = editorRef.value;
     if (editor == null) return;
 
     editor.destroy();
-  });
+});
 </script>
 
 <style lang="scss" scoped>
-  .sa-editor {
+.sa-editor {
     border: 1px solid var(--sa-border);
+
     :deep() {
-      .w-e-bar {
-        border-bottom: 1px solid var(--sa-border);
-      }
+        .w-e-bar {
+            border-bottom: 1px solid var(--sa-border);
+        }
+    }
+}
+</style>
+
+<style lang="scss">
+// 全局样式覆盖,确保富文本编辑器的加粗等样式正常显示
+.w-e-text-container [data-slate-editor] {
+
+    strong,
+    strong.token.bold,
+    b {
+        font-weight: bold !important;
+    }
+
+    em,
+    i {
+        font-style: italic !important;
+    }
+
+    u {
+        text-decoration: underline !important;
+    }
+
+    s,
+    strike {
+        text-decoration: line-through !important;
     }
-  }
+}
 </style>