選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。
 
 
 
 
 
 

282 行
10 KiB

  1. {% extends "../layout.html" %}
  2. {% block title %}网站配置{% endblock %}
  3. {% block content %}
  4. <div id="configApp">
  5. <el-tabs v-model="activeTab" type="border-card">
  6. <!-- 基础设置 -->
  7. <el-tab-pane label="基础设置" name="basic">
  8. <el-form :model="form.basic" label-width="120px" class="max-w-2xl">
  9. <el-form-item label="站点名称">
  10. <el-input v-model="form.basic.site_name" placeholder="请输入站点名称" style="width:400px;"></el-input>
  11. </el-form-item>
  12. <el-form-item label="站点副标题">
  13. <el-input v-model="form.basic.site_subtitle" placeholder="请输入站点副标题" style="width:400px;"></el-input>
  14. </el-form-item>
  15. <el-form-item label="网站域名">
  16. <el-input v-model="form.basic.site_domain" placeholder="请输入网站域名" style="width:400px;" disabled></el-input>
  17. </el-form-item>
  18. <el-form-item label="ICP备案号">
  19. <el-input v-model="form.basic.icp_number" placeholder="请输入ICP备案号" style="width:400px;"></el-input>
  20. </el-form-item>
  21. <el-form-item label="版权信息">
  22. <el-input v-model="form.basic.copyright" placeholder="请输入版权信息" style="width:400px;"></el-input>
  23. </el-form-item>
  24. <el-form-item label="网站Logo">
  25. <div class="flex items-center gap-3">
  26. <div class="upload-box" @click="handleUpload('basic', 'logo')">
  27. <img v-if="form.basic.logo" :src="form.basic.logo" class="upload-preview">
  28. <div v-else class="upload-placeholder">
  29. <el-icon :size="28"><Plus /></el-icon>
  30. <span>点击上传</span>
  31. </div>
  32. </div>
  33. <span class="text-xs text-gray-400">建议尺寸: 200×60px,PNG格式</span>
  34. </div>
  35. </el-form-item>
  36. <el-form-item label="公众号二维码">
  37. <div class="flex items-center gap-3">
  38. <div class="upload-box upload-box-square" @click="handleUpload('basic', 'wechat_qrcode')">
  39. <img v-if="form.basic.wechat_qrcode" :src="form.basic.wechat_qrcode" class="upload-preview">
  40. <div v-else class="upload-placeholder">
  41. <el-icon :size="28"><Plus /></el-icon>
  42. <span>上传二维码</span>
  43. </div>
  44. </div>
  45. <span class="text-xs text-gray-400">建议尺寸: 300×300px</span>
  46. </div>
  47. </el-form-item>
  48. <el-form-item>
  49. <el-button type="primary" @click="saveConfig('basic')" :loading="saving.basic">保存配置</el-button>
  50. </el-form-item>
  51. </el-form>
  52. </el-tab-pane>
  53. <!-- SEO设置 -->
  54. <el-tab-pane label="SEO设置" name="seo">
  55. <el-form :model="form.seo" label-width="140px" class="max-w-2xl">
  56. <el-form-item label="页面标题(Title)">
  57. <el-input v-model="form.seo.page_title" placeholder="请输入页面标题" style="width:400px;"></el-input>
  58. </el-form-item>
  59. <el-form-item label="关键词(Keywords)">
  60. <el-input v-model="form.seo.keywords" placeholder="请输入关键词,用逗号分隔" style="width:400px;"></el-input>
  61. </el-form-item>
  62. <el-form-item label="描述(Description)">
  63. <el-input v-model="form.seo.description" type="textarea" :rows="3" placeholder="请输入页面描述" style="width:400px;"></el-input>
  64. </el-form-item>
  65. <el-form-item label="Favicon">
  66. <div class="flex items-center gap-3">
  67. <div class="upload-box upload-box-small" @click="handleUpload('seo', 'favicon')">
  68. <img v-if="form.seo.favicon" :src="form.seo.favicon" class="upload-preview">
  69. <div v-else class="upload-placeholder">
  70. <el-icon :size="20"><Plus /></el-icon>
  71. <span>上传</span>
  72. </div>
  73. </div>
  74. <span class="text-xs text-gray-400">建议尺寸: 32×32px,ICO/PNG格式</span>
  75. </div>
  76. </el-form-item>
  77. <el-form-item>
  78. <el-button type="primary" @click="saveConfig('seo')" :loading="saving.seo">保存配置</el-button>
  79. </el-form-item>
  80. </el-form>
  81. </el-tab-pane>
  82. <!-- 联系信息 -->
  83. <el-tab-pane label="联系信息" name="contact">
  84. <el-form :model="form.contact" label-width="140px" class="max-w-2xl">
  85. <el-form-item label="联系电话">
  86. <el-input v-model="form.contact.phone" placeholder="请输入联系电话" style="width:400px;"></el-input>
  87. </el-form-item>
  88. <el-form-item label="联系邮箱">
  89. <el-input v-model="form.contact.email" placeholder="请输入联系邮箱" style="width:400px;"></el-input>
  90. </el-form-item>
  91. <el-form-item label="办公地址">
  92. <el-input v-model="form.contact.address" placeholder="请输入办公地址" style="width:400px;"></el-input>
  93. </el-form-item>
  94. <el-form-item label="邮政编码">
  95. <el-input v-model="form.contact.postcode" placeholder="请输入邮政编码" style="width:400px;"></el-input>
  96. </el-form-item>
  97. <el-form-item label="地图坐标(经度)">
  98. <el-input v-model="form.contact.map_lng" placeholder="如: 116.480881" style="width:400px;"></el-input>
  99. </el-form-item>
  100. <el-form-item label="地图坐标(纬度)">
  101. <el-input v-model="form.contact.map_lat" placeholder="如: 39.948692" style="width:400px;"></el-input>
  102. </el-form-item>
  103. <el-form-item>
  104. <el-button type="primary" @click="saveConfig('contact')" :loading="saving.contact">保存配置</el-button>
  105. </el-form-item>
  106. </el-form>
  107. </el-tab-pane>
  108. <!-- 社交媒体 -->
  109. <el-tab-pane label="社交媒体" name="social">
  110. <el-form :model="form.social" label-width="140px" class="max-w-2xl">
  111. <el-form-item label="微信公众号名称">
  112. <el-input v-model="form.social.wechat_name" placeholder="请输入公众号名称" style="width:400px;"></el-input>
  113. </el-form-item>
  114. <el-form-item label="微信公众号ID">
  115. <el-input v-model="form.social.wechat_id" placeholder="请输入公众号微信号" style="width:400px;"></el-input>
  116. </el-form-item>
  117. <el-form-item label="微博链接">
  118. <el-input v-model="form.social.weibo_url" placeholder="请输入微博主页链接" style="width:400px;"></el-input>
  119. </el-form-item>
  120. <el-form-item label="抖音链接">
  121. <el-input v-model="form.social.douyin_url" placeholder="请输入抖音主页链接" style="width:400px;"></el-input>
  122. </el-form-item>
  123. <el-form-item label="视频号链接">
  124. <el-input v-model="form.social.video_url" placeholder="请输入视频号链接" style="width:400px;"></el-input>
  125. </el-form-item>
  126. <el-form-item>
  127. <el-button type="primary" @click="saveConfig('social')" :loading="saving.social">保存配置</el-button>
  128. </el-form-item>
  129. </el-form>
  130. </el-tab-pane>
  131. </el-tabs>
  132. </div>
  133. {% endblock %}
  134. {% block css %}
  135. <style>
  136. .upload-box {
  137. width: 160px;
  138. height: 80px;
  139. border: 2px dashed var(--el-border-color);
  140. border-radius: 6px;
  141. display: flex;
  142. flex-direction: column;
  143. align-items: center;
  144. justify-content: center;
  145. cursor: pointer;
  146. transition: all 0.2s;
  147. overflow: hidden;
  148. }
  149. .upload-box:hover {
  150. border-color: var(--el-color-primary);
  151. color: var(--el-color-primary);
  152. }
  153. .upload-box-square {
  154. width: 120px;
  155. height: 120px;
  156. }
  157. .upload-box-small {
  158. width: 80px;
  159. height: 80px;
  160. }
  161. .upload-placeholder {
  162. display: flex;
  163. flex-direction: column;
  164. align-items: center;
  165. gap: 4px;
  166. color: var(--el-text-color-placeholder);
  167. font-size: 12px;
  168. }
  169. .upload-preview {
  170. width: 100%;
  171. height: 100%;
  172. object-fit: contain;
  173. }
  174. </style>
  175. {% endblock %}
  176. {% block js %}
  177. <script>
  178. const configsData = {{ configs | dump | safe }};
  179. const { createApp, ref, reactive, onMounted } = Vue;
  180. const app = createApp({
  181. setup() {
  182. const activeTab = ref('basic');
  183. const saving = reactive({ basic: false, seo: false, contact: false, social: false });
  184. // 将配置数组转为对象
  185. function arrayToObject(arr) {
  186. const obj = {};
  187. if (arr && Array.isArray(arr)) {
  188. arr.forEach(item => {
  189. obj[item.key] = item.value || '';
  190. });
  191. }
  192. return obj;
  193. }
  194. const form = reactive({
  195. basic: arrayToObject(configsData.basic),
  196. seo: arrayToObject(configsData.seo),
  197. contact: arrayToObject(configsData.contact),
  198. social: arrayToObject(configsData.social)
  199. });
  200. // 保存配置
  201. async function saveConfig(group) {
  202. saving[group] = true;
  203. try {
  204. const res = await fetch('/admin/system/config/save', {
  205. method: 'POST',
  206. headers: { 'Content-Type': 'application/json' },
  207. body: JSON.stringify({ group, data: form[group] })
  208. }).then(r => r.json());
  209. if (res.code === 0) {
  210. ElementPlus.ElMessage.success('保存成功');
  211. } else {
  212. ElementPlus.ElMessage.error(res.msg || '保存失败');
  213. }
  214. } finally {
  215. saving[group] = false;
  216. }
  217. }
  218. // 上传图片
  219. function handleUpload(group, key) {
  220. const input = document.createElement('input');
  221. input.type = 'file';
  222. input.accept = 'image/*';
  223. input.onchange = async (e) => {
  224. const file = e.target.files[0];
  225. if (!file) return;
  226. const formData = new FormData();
  227. formData.append('file', file);
  228. try {
  229. ElementPlus.ElMessage.info('上传中...');
  230. const res = await fetch('/admin/upload', {
  231. method: 'POST',
  232. body: formData
  233. }).then(r => r.json());
  234. if (res.code === 0) {
  235. form[group][key] = res.data.url;
  236. ElementPlus.ElMessage.success('上传成功');
  237. } else {
  238. ElementPlus.ElMessage.error(res.msg || '上传失败');
  239. }
  240. } catch (err) {
  241. ElementPlus.ElMessage.error('上传失败: ' + err.message);
  242. }
  243. };
  244. input.click();
  245. }
  246. return {
  247. activeTab,
  248. form,
  249. saving,
  250. saveConfig,
  251. handleUpload
  252. };
  253. }
  254. });
  255. app.use(ElementPlus, { locale: ElementPlusLocaleZhCn });
  256. // 注册 Element Plus Icons
  257. for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  258. app.component(key, component);
  259. }
  260. app.mount('#configApp');
  261. </script>
  262. {% endblock %}