mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-07-27 04:20:43 +08:00
Compare commits
196 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 21eeb912fd | |||
| a60def91a0 | |||
| 759373e327 | |||
| 7d74a40f51 | |||
| 50cad268d3 | |||
| 8510b71ff6 | |||
| a012a57cf0 | |||
| d8ad0772bf | |||
| f105dedc23 | |||
| c847dfb1c4 | |||
| 862d176593 | |||
| 3945ee611d | |||
| de142c4729 | |||
| 0b4090f9e9 | |||
| 3ccd4f7ccf | |||
| 6f01501fd1 | |||
| e426abf336 | |||
| a767397e23 | |||
| 17f5b4b917 | |||
| 94ff37f77f | |||
| 73bc88bad8 | |||
| 72dc278a2b | |||
| 7ba35ffd5d | |||
| 3643bdf6f6 | |||
| 21fee2dc9a | |||
| 90f6516f45 | |||
| a15621d55e | |||
| a717beb0e7 | |||
| b9ad94ddb5 | |||
| 9ab013562d | |||
| a8bd121deb | |||
| 476a1ec48e | |||
| 31ade9908a | |||
| 5606e8c59f | |||
| 61d3b22fdb | |||
| c05aa5253c | |||
| 0b599dcced | |||
| 623e3fa59a | |||
| 1075f588fa | |||
| c0d8cb2c3b | |||
| 9c6a054b12 | |||
| 6a05f664ba | |||
| 9c4d2242ae | |||
| eac027c116 | |||
| 3a08008192 | |||
| 7daf93d51f | |||
| 409be28deb | |||
| 97e358e41f | |||
| c17e668afe | |||
| 4611932ad8 | |||
| 452d021816 | |||
| d71022c224 | |||
| d521a641de | |||
| 4246d8cdcb | |||
| 6989fd8ddc | |||
| 742b51a23e | |||
| bc541a3ef6 | |||
| 67fc682164 | |||
| b43dd7e5a7 | |||
| e29ccf65c5 | |||
| 91553fc053 | |||
| 22f57b7860 | |||
| 2a25727c0b | |||
| eb69ae4af6 | |||
| 2fd83a7814 | |||
| 8963ba1921 | |||
| 051c72b584 | |||
| 552827eaac | |||
| e8d13c674f | |||
| 256bc0e00f | |||
| 9aa0e310f2 | |||
| 6a0bb0e07f | |||
| 044f737016 | |||
| 27b3d31828 | |||
| 64c9a15d51 | |||
| df4c58d3e4 | |||
| 93f23696ef | |||
| 2ed7e1a260 | |||
| e19b107ed0 | |||
| 7c15c84dba | |||
| e8edc03526 | |||
| 2b717ad449 | |||
| 5efc877a89 | |||
| 75a133415a | |||
| bf41b08b35 | |||
| 74a5efb596 | |||
| 40dd4f4b7f | |||
| 341670adcf | |||
| 115aea2580 | |||
| 58f1e7f131 | |||
| 26ce84b950 | |||
| 32b3bbaa74 | |||
| 76c00f31f6 | |||
| e28ea6ca08 | |||
| a98254e6cc | |||
| 82062d9465 | |||
| 706cee2240 | |||
| da60eb2daf | |||
| a764c3dbfe | |||
| 88fb74a795 | |||
| 52d4b7927b | |||
| ff31e0d8e8 | |||
| a65ea2617b | |||
| bb228c6705 | |||
| 62992b2688 | |||
| da141ae10a | |||
| d22224e2fc | |||
| d0b3a42c1d | |||
| c3349ae070 | |||
| ad6719e201 | |||
| 0e342d5fa3 | |||
| daf1318a90 | |||
| d4ccc9b7d6 | |||
| ce60c334ec | |||
| ca723bae80 | |||
| c312fab4b1 | |||
| d9db03959a | |||
| add84f6e4b | |||
| 850ee73fa2 | |||
| 77da85f7d4 | |||
| 4d0901b650 | |||
| 66a39dd723 | |||
| 4cdcbb1958 | |||
| e0569e287f | |||
| b42932ddad | |||
| cc05b64dec | |||
| aec54e8c5b | |||
| 15e95a2f3e | |||
| 74e5c9c3cf | |||
| 85227a8aa4 | |||
| e7a00c5b69 | |||
| 85e558f1a0 | |||
| fade65397d | |||
| a2bd2cc8ba | |||
| bdaeb98432 | |||
| ccefbcee02 | |||
| e5590c0a22 | |||
| a85131308c | |||
| e17edade28 | |||
| 959acab9c7 | |||
| 0b59f69fa0 | |||
| ab755a8623 | |||
| a67ad88c3f | |||
| 6684f80f02 | |||
| 620354b77a | |||
| 19c4e12a5b | |||
| 3b0ef59ca7 | |||
| 0386c66b70 | |||
| 9721932c34 | |||
| f9fce7d0ed | |||
| 4747d593be | |||
| 7d8e4ad559 | |||
| 3d02b8cc44 | |||
| 595c2d7cb7 | |||
| 2154daea57 | |||
| 85f166e7d0 | |||
| 0d618e21ce | |||
| 8d610473b3 | |||
| 512a4013b9 | |||
| f6069cad95 | |||
| 90ef890766 | |||
| 5727efa820 | |||
| 057a7cf6f0 | |||
| a81c506a14 | |||
| 70dc7cd0a7 | |||
| ee28753094 | |||
| 91923c0b42 | |||
| f6feecb324 | |||
| 6fdaeed792 | |||
| f838da43d8 | |||
| 21d723d260 | |||
| d701867b83 | |||
| 66f4ccffc0 | |||
| 9720743c80 | |||
| 6ba87b1972 | |||
| a0f3d1b220 | |||
| 6e80fa14c8 | |||
| 22a8e92dc2 | |||
| 6b1e0e5cc0 | |||
| 261617fc12 | |||
| 5000da9a64 | |||
| 9573b303e2 | |||
| f12262f4df | |||
| 1305b4a555 | |||
| b7e2025ebb | |||
| 0da5feb2ee | |||
| e46c5c2b6f | |||
| 70c5b34de0 | |||
| 9d3ebac076 | |||
| 3fd3b2247a | |||
| 3702cb1618 | |||
| 473655171b | |||
| 9e2f090747 | |||
| e05d0d7ce5 | |||
| 3ba10fa105 | |||
| acc9070134 |
@@ -0,0 +1,8 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets).
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in
|
||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.1.3/schema.json",
|
||||
"changelog": "@changesets/cli/changelog",
|
||||
"commit": false,
|
||||
"fixed": [],
|
||||
"linked": [],
|
||||
"access": "restricted",
|
||||
"baseBranch": "base",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# API 和 HTTP 请求规范
|
||||
|
||||
## HTTP 请求封装
|
||||
- 可以使用 `简单http` 或者 `alova` 或者 `@tanstack/vue-query` 进行请求管理
|
||||
- HTTP 配置在 [src/http/](mdc:src/http/) 目录下
|
||||
- `简单http` - [src/http/http.ts](mdc:src/http/http.ts)
|
||||
- `alova` - [src/http/alova.ts](mdc:src/http/alova.ts)
|
||||
- `vue-query` - [src/http/vue-query.ts](mdc:src/http/vue-query.ts)
|
||||
- 请求拦截器在 [src/http/interceptor.ts](mdc:src/http/interceptor.ts)
|
||||
- 支持请求重试、缓存、错误处理
|
||||
|
||||
## API 接口规范
|
||||
- API 接口定义在 [src/api/](mdc:src/api/) 目录下
|
||||
- 按功能模块组织 API 文件
|
||||
- 使用 TypeScript 定义请求和响应类型
|
||||
- 支持 `简单http`、`alova` 和 `vue-query` 三种请求方式
|
||||
|
||||
|
||||
## 示例代码结构
|
||||
```typescript
|
||||
// API 接口定义
|
||||
export interface LoginParams {
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
token: string
|
||||
userInfo: UserInfo
|
||||
}
|
||||
|
||||
// alova 方式
|
||||
export const login = (params: LoginParams) =>
|
||||
http.Post<LoginResponse>('/api/login', params)
|
||||
|
||||
// vue-query 方式
|
||||
export const useLogin = () => {
|
||||
return useMutation({
|
||||
mutationFn: (params: LoginParams) =>
|
||||
http.post<LoginResponse>('/api/login', params)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
- 统一错误处理在拦截器中配置
|
||||
- 支持网络错误、业务错误、认证错误等
|
||||
- 自动处理 token 过期和刷新
|
||||
---
|
||||
globs: src/api/*.ts,src/http/*.ts
|
||||
---
|
||||
@@ -0,0 +1,43 @@
|
||||
# 开发工作流程
|
||||
|
||||
## 项目启动
|
||||
1. 安装依赖:`pnpm install`
|
||||
2. 开发环境:
|
||||
- H5: `pnpm dev` 或 `pnpm dev:h5`
|
||||
- 微信小程序: `pnpm dev:mp`
|
||||
- 支付宝小程序: `pnpm dev:mp-alipay`
|
||||
- APP: `pnpm dev:app`
|
||||
|
||||
## 代码规范
|
||||
- 使用 ESLint 进行代码检查:`pnpm lint`
|
||||
- 自动修复代码格式:`pnpm lint:fix`
|
||||
- 使用 eslint 格式化代码
|
||||
- 遵循 TypeScript 严格模式
|
||||
|
||||
## 构建和部署
|
||||
- H5 构建:`pnpm build:h5`
|
||||
- 微信小程序构建:`pnpm build:mp`
|
||||
- 支付宝小程序构建:`pnpm build:mp-alipay`
|
||||
- APP 构建:`pnpm build:app`
|
||||
- 类型检查:`pnpm type-check`
|
||||
|
||||
## 开发工具
|
||||
- 推荐使用 VSCode 编辑器
|
||||
- 安装 Vue 和 TypeScript 相关插件
|
||||
- 使用 uni-app 开发者工具调试小程序
|
||||
- 使用 HBuilderX 调试 APP
|
||||
|
||||
## 调试技巧
|
||||
- 使用 console.log 和 uni.showToast 调试
|
||||
- 利用 Vue DevTools 调试组件状态
|
||||
- 使用网络面板调试 API 请求
|
||||
- 平台差异测试和兼容性检查
|
||||
|
||||
## 性能优化
|
||||
- 使用懒加载和代码分割
|
||||
- 优化图片和静态资源
|
||||
- 减少不必要的重渲染
|
||||
- 合理使用缓存策略
|
||||
---
|
||||
description: 开发工作流程和最佳实践指南
|
||||
---
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
# 项目概览
|
||||
|
||||
这是一个基于 uniapp + Vue3 + TypeScript + Vite5 + UnoCSS 的跨平台开发框架。
|
||||
|
||||
## 项目特点
|
||||
- 支持 H5、小程序、APP 多平台开发
|
||||
- 使用最新的前端技术栈
|
||||
- 内置约定式路由、layout布局、请求封装、登录拦截、自定义tabbar等功能
|
||||
- 无需依赖 HBuilderX,支持命令行开发
|
||||
|
||||
## 核心配置文件
|
||||
- [package.json](mdc:package.json) - 项目依赖和脚本配置
|
||||
- [vite.config.ts](mdc:vite.config.ts) - Vite 构建配置
|
||||
- [pages.config.ts](mdc:pages.config.ts) - 页面路由配置
|
||||
- [manifest.config.ts](mdc:manifest.config.ts) - 应用清单配置
|
||||
- [uno.config.ts](mdc:uno.config.ts) - UnoCSS 配置
|
||||
|
||||
## 主要目录结构
|
||||
- `src/pages/` - 页面文件
|
||||
- `src/components/` - 组件文件
|
||||
- `src/layouts/` - 布局文件
|
||||
- `src/api/` - API 接口
|
||||
- `src/http/` - HTTP 请求封装
|
||||
- `src/store/` - 状态管理
|
||||
- `src/tabbar/` - 底部导航栏
|
||||
- `src/App.ku.vue` - 全局根组件(类似 App.vue 里面的 template作用)
|
||||
|
||||
## 开发命令
|
||||
- `pnpm dev` - 开发 H5 版本
|
||||
- `pnpm dev:mp` - 开发微信小程序
|
||||
- `pnpm dev:mp-alipay` - 开发支付宝小程序(含钉钉)
|
||||
- `pnpm dev:app` - 开发 APP 版本
|
||||
- `pnpm build` - 构建生产版本
|
||||
@@ -0,0 +1,54 @@
|
||||
# 样式和 CSS 开发规范
|
||||
|
||||
## UnoCSS 原子化 CSS
|
||||
- 项目使用 UnoCSS 作为原子化 CSS 框架
|
||||
- 配置在 [uno.config.ts](mdc:uno.config.ts)
|
||||
- 支持预设和自定义规则
|
||||
- 优先使用原子化类名,减少自定义 CSS
|
||||
|
||||
## SCSS 规范
|
||||
- 使用 SCSS 预处理器
|
||||
- 样式文件使用 `lang="scss"` 和 `scoped` 属性
|
||||
- 遵循 BEM 命名规范
|
||||
- 使用变量和混入提高复用性
|
||||
|
||||
## 样式组织
|
||||
- 全局样式在 [src/style/](mdc:src/style/) 目录下
|
||||
- 组件样式使用 scoped 作用域
|
||||
- 图标字体在 [src/style/iconfont.css](mdc:src/style/iconfont.css)
|
||||
- 主题变量在 [src/uni_modules/uni-scss/](mdc:src/uni_modules/uni-scss/) 目录下
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<template>
|
||||
<view class="container flex flex-col items-center p-4">
|
||||
<text class="title text-lg font-bold mb-2">标题</text>
|
||||
<view class="content bg-gray-100 rounded-lg p-3">
|
||||
<!-- 内容 -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
|
||||
.title {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
max-width: 600rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
## 响应式设计
|
||||
- 使用 rpx 单位适配不同屏幕
|
||||
- 支持横屏和竖屏布局
|
||||
- 使用 flexbox 和 grid 布局
|
||||
- 考虑不同平台的样式差异
|
||||
---
|
||||
globs: *.vue,*.scss,*.css
|
||||
---
|
||||
@@ -0,0 +1,63 @@
|
||||
# uni-app 开发规范
|
||||
|
||||
## 页面开发
|
||||
- 页面文件放在 [src/pages/](mdc:src/pages/) 目录下
|
||||
- 使用约定式路由,文件名即路由路径
|
||||
- 页面配置在仅需要在 宏`definePage` 中配置标题等内容即可,会自动生成到 `pages.json` 中
|
||||
- definePage的顺序在最上面
|
||||
|
||||
## 组件开发
|
||||
- 组件文件放在 [src/components/](mdc:src/components/) 或者 [src/pages/xx/components/](mdc:src/pages/xx/components/) 目录下
|
||||
- 使用 uni-app 内置组件和第三方组件库
|
||||
- 支持 wot-ui\uview-pro\uv-ui\sard-ui\uview-plus 等多种第三方组件库 和 z-paging 组件
|
||||
- 自定义组件遵循 uni-app 组件规范
|
||||
|
||||
## 平台适配
|
||||
- 使用条件编译处理平台差异
|
||||
- 支持 H5、小程序、APP 多平台
|
||||
- 注意各平台的 API 差异
|
||||
- 使用 uni.xxx API 替代原生 API
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// #ifdef H5
|
||||
import { h5Api } from '@/utils/h5'
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
import { mpApi } from '@/utils/mp'
|
||||
// #endif
|
||||
|
||||
const handleClick = () => {
|
||||
// #ifdef H5
|
||||
h5Api.showToast('H5 平台')
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
mpApi.showToast('微信小程序')
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- uni-app 组件 -->
|
||||
<button @click="handleClick">点击</button>
|
||||
|
||||
<!-- 条件渲染 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view>H5 特有内容</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 生命周期
|
||||
- 使用 uni-app 页面生命周期
|
||||
- onLoad、onShow、onReady、onHide、onUnload
|
||||
- 组件生命周期遵循 Vue3 规范
|
||||
- 注意页面栈和导航管理
|
||||
---
|
||||
globs: src/pages/*.vue,src/components/*.vue
|
||||
---
|
||||
@@ -0,0 +1,53 @@
|
||||
# Vue3 + TypeScript 开发规范
|
||||
|
||||
## Vue 组件规范
|
||||
- 使用 Composition API 和 `<script setup>` 语法
|
||||
- 组件文件使用 PascalCase 命名
|
||||
- 页面文件放在 `src/pages/` 目录下
|
||||
- 全局组件文件放在 `src/components/` 目录下
|
||||
- 局部组件文件放在页面的 `/components/` 目录下
|
||||
|
||||
## Vue SFC 组件规范
|
||||
- `<script setup lang="ts">` 标签必须是第一个子元素
|
||||
- `<template>` 标签必须是第二个子元素
|
||||
- `<style scoped>` 标签必须是最后一个子元素(因为推荐使用原子化类名,所以很可能没有)
|
||||
|
||||
## TypeScript 规范
|
||||
- 严格使用 TypeScript,避免使用 `any` 类型
|
||||
- 为 API 响应数据定义接口类型
|
||||
- 使用 `interface` 定义对象类型,`type` 定义联合类型
|
||||
- 导入类型时使用 `import type` 语法
|
||||
|
||||
## 状态管理
|
||||
- 使用 Pinia 进行状态管理
|
||||
- Store 文件放在 `src/store/` 目录下
|
||||
- 使用 `defineStore` 定义 store
|
||||
- 支持持久化存储
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import type { UserInfo } from '@/types/user'
|
||||
|
||||
const userInfo = ref<UserInfo | null>(null)
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化逻辑
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="container">
|
||||
<!-- 模板内容 -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
// 样式
|
||||
}
|
||||
</style>
|
||||
---
|
||||
globs: *.vue,*.ts,*.tsx
|
||||
---
|
||||
@@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*] # 表示所有文件适用
|
||||
charset = utf-8 # 设置文件字符集为 utf-8
|
||||
indent_style = space # 缩进风格(tab | space)
|
||||
indent_size = 2 # 缩进大小
|
||||
end_of_line = lf # 控制换行类型(lf | cr | crlf)
|
||||
trim_trailing_whitespace = true # 去除行首的任意空白字符
|
||||
insert_final_newline = true # 始终在文件末尾插入一个新行
|
||||
|
||||
[*.md] # 表示仅 md 文件适用以下规则
|
||||
max_line_length = off # 关闭最大行长度限制
|
||||
trim_trailing_whitespace = false # 关闭末尾空格修剪
|
||||
+48
-5
@@ -1,5 +1,48 @@
|
||||
node_modules/
|
||||
unpackage/
|
||||
config/halo.config.js
|
||||
config/ad.config.js
|
||||
package-lock.json
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
.hbuilderx
|
||||
|
||||
.stylelintcache
|
||||
.eslintcache
|
||||
|
||||
docs/.vitepress/dist
|
||||
docs/.vitepress/cache
|
||||
|
||||
src/types
|
||||
# 单独把这个文件排除掉,用以解决部分电脑生成的 auto-import.d.ts 的API不完整导致类型提示报错问题
|
||||
!src/types/auto-import.d.ts
|
||||
src/manifest.json
|
||||
src/pages.json
|
||||
|
||||
# 2025-10-15 by 菲鸽: lock 文件还是需要加入版本管理,今天又遇到版本不一致导致无法运行的问题了。
|
||||
# pnpm-lock.yaml
|
||||
# package-lock.json
|
||||
|
||||
# TIPS:如果某些文件已经加入了版本管理,现在重新加入 .gitignore 是不生效的,需要执行下面的操作
|
||||
# `git rm -r --cached .` 然后提交 commit 即可。
|
||||
|
||||
# git rm -r --cached file1 file2 ## 针对某些文件
|
||||
# git rm -r --cached dir1 dir2 ## 针对某些文件夹
|
||||
# git rm -r --cached . ## 针对所有文件
|
||||
|
||||
# 更新 uni-app 官方版本
|
||||
# npx @dcloudio/uvm@latest
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
|
||||
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
|
||||
"version": "0.0",
|
||||
"configurations": [{
|
||||
"app-plus" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"default" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"mp-weixin" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"type" : "uniCloud"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
npx --no-install commitlint --edit "$1"
|
||||
@@ -0,0 +1 @@
|
||||
npx lint-staged --allow-empty
|
||||
@@ -0,0 +1,9 @@
|
||||
# registry = https://registry.npmjs.org
|
||||
registry = https://registry.npmmirror.com
|
||||
|
||||
strict-peer-dependencies=false
|
||||
auto-install-peers=true
|
||||
shamefully-hoist=true
|
||||
ignore-workspace-root-check=true
|
||||
install-workspace-root=true
|
||||
node-options=--max-old-space-size=8192
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
scene: git_message
|
||||
---
|
||||
|
||||
在此处编写规则,自定义 AI 生成提交信息的风格。
|
||||
|
||||
使用中文编写提交信息
|
||||
@@ -0,0 +1,123 @@
|
||||
# 项目概览
|
||||
|
||||
这是一个基于 uniapp + Vue3 + TypeScript + Vite5 + UnoCSS 的跨平台开发框架。
|
||||
|
||||
## 项目特点
|
||||
- 支持 H5、小程序、APP 多平台开发
|
||||
- 使用最新的前端技术栈
|
||||
- 内置约定式路由、layout布局、请求封装等功能
|
||||
- 无需依赖 HBuilderX,支持命令行开发
|
||||
|
||||
## 核心配置文件
|
||||
- [package.json](mdc:package.json) - 项目依赖和脚本配置
|
||||
- [vite.config.ts](mdc:vite.config.ts) - Vite 构建配置
|
||||
- [pages.config.ts](mdc:pages.config.ts) - 页面路由配置
|
||||
- [manifest.config.ts](mdc:manifest.config.ts) - 应用清单配置
|
||||
- [uno.config.ts](mdc:uno.config.ts) - UnoCSS 配置
|
||||
|
||||
## 主要目录结构
|
||||
- `src/pages/` - 页面文件
|
||||
- `src/components/` - 组件文件
|
||||
- `src/layouts/` - 布局文件
|
||||
- `src/api/` - API 接口
|
||||
- `src/http/` - HTTP 请求封装
|
||||
- `src/store/` - 状态管理
|
||||
- `src/tabbar/` - 底部导航栏
|
||||
- `src/App.ku.vue` - 全局根组件(类似 App.vue 里面的 template作用)
|
||||
|
||||
## 开发命令
|
||||
- `pnpm dev` - 开发 H5 版本
|
||||
- `pnpm dev:mp` - 开发微信小程序
|
||||
- `pnpm dev:mp-alipay` - 开发支付宝小程序(含钉钉)
|
||||
- `pnpm dev:app` - 开发 APP 版本
|
||||
- `pnpm build` - 构建生产版本
|
||||
|
||||
## Vue 组件规范
|
||||
- 使用 Composition API 和 `<script setup>` 语法
|
||||
- 组件文件使用 PascalCase 命名
|
||||
- 页面文件放在 `src/pages/` 目录下
|
||||
- 全局组件文件放在 `src/components/` 目录下
|
||||
- 局部组件文件放在页面的 `/components/` 目录下
|
||||
|
||||
## TypeScript 规范
|
||||
- 严格使用 TypeScript,避免使用 `any` 类型
|
||||
- 为 API 响应数据定义接口类型
|
||||
- 使用 `interface` 定义对象类型,`type` 定义联合类型
|
||||
- 导入类型时使用 `import type` 语法
|
||||
|
||||
## 状态管理
|
||||
- 使用 Pinia 进行状态管理
|
||||
- Store 文件放在 `src/store/` 目录下
|
||||
- 使用 `defineStore` 定义 store
|
||||
- 支持持久化存储
|
||||
|
||||
## UnoCSS 原子化 CSS
|
||||
- 项目使用 UnoCSS 作为原子化 CSS 框架
|
||||
- 配置在 [uno.config.ts]
|
||||
- 支持预设和自定义规则
|
||||
- 优先使用原子化类名,减少自定义 CSS
|
||||
|
||||
## Vue SFC 组件规范
|
||||
- `<script setup lang="ts">` 标签必须是第一个子元素
|
||||
- `<template>` 标签必须是第二个子元素
|
||||
- `<style scoped>` 标签必须是最后一个子元素(因为推荐使用原子化类名,所以很可能没有)
|
||||
|
||||
## 页面开发
|
||||
- 页面文件放在 [src/pages/]目录下
|
||||
- 使用约定式路由,文件名即路由路径
|
||||
- 页面配置在仅需要在 宏`definePage` 中配置标题等内容即可,会自动生成到 `pages.json` 中
|
||||
- definePage的顺序在最上面
|
||||
|
||||
## 组件开发
|
||||
- 全局组件文件放在 `src/components/` 目录下
|
||||
- 局部组件文件放在页面的 `/components/` 目录下
|
||||
- 使用 uni-app 内置组件和第三方组件库
|
||||
- 支持 wot-ui\uview-pro\uv-ui\sard-ui\uview-plus 等多种第三方组件库 和 z-paging 组件
|
||||
- 自定义组件遵循 uni-app 组件规范
|
||||
|
||||
## 平台适配
|
||||
- 使用条件编译处理平台差异
|
||||
- 支持 H5、小程序、APP 多平台
|
||||
- 注意各平台的 API 差异
|
||||
- 使用 uni.xxx API 替代原生 API
|
||||
|
||||
## 示例代码结构
|
||||
```vue
|
||||
<script setup lang="ts">
|
||||
// #ifdef H5
|
||||
import { h5Api } from '@/utils/h5'
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
import { mpApi } from '@/utils/mp'
|
||||
// #endif
|
||||
|
||||
const handleClick = () => {
|
||||
// #ifdef H5
|
||||
h5Api.showToast('H5 平台')
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
mpApi.showToast('微信小程序')
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="page">
|
||||
<!-- uni-app 组件 -->
|
||||
<button @click="handleClick">点击</button>
|
||||
|
||||
<!-- 条件渲染 -->
|
||||
<!-- #ifdef H5 -->
|
||||
<view>H5 特有内容</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
```
|
||||
|
||||
## 生命周期
|
||||
- 使用 uni-app 页面生命周期
|
||||
- onLoad、onShow、onReady、onHide、onUnload
|
||||
- 组件生命周期遵循 Vue3 规范
|
||||
- 注意页面栈和导航管理
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"antfu.unocss",
|
||||
"antfu.iconify",
|
||||
"evils.uniapp-vscode",
|
||||
"uni-helper.uni-helper-vscode",
|
||||
"uni-helper.uni-app-schemas-vscode",
|
||||
"uni-helper.uni-highlight-vscode",
|
||||
"uni-helper.uni-ui-snippets-vscode",
|
||||
"uni-helper.uni-app-snippets-vscode",
|
||||
"streetsidesoftware.code-spell-checker"
|
||||
]
|
||||
}
|
||||
Vendored
+107
@@ -0,0 +1,107 @@
|
||||
{
|
||||
// 配置语言的文件关联
|
||||
"files.associations": {
|
||||
"pages.json": "jsonc", // pages.json 可以写注释
|
||||
"manifest.json": "jsonc" // manifest.json 可以写注释
|
||||
},
|
||||
|
||||
"stylelint.enable": false, // 禁用 stylelint
|
||||
"css.validate": false, // 禁用 CSS 内置验证
|
||||
"scss.validate": false, // 禁用 SCSS 内置验证
|
||||
"less.validate": false, // 禁用 LESS 内置验证
|
||||
|
||||
// 新版本 VsCode 中这个配置已失效
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
|
||||
// 配置新版本 VsCode 工作区的 TypeScript 的版本
|
||||
"js/ts.tsdk.path": "node_modules/typescript/lib",
|
||||
"js/ts.tsdk.promptToUseWorkspaceVersion": true,
|
||||
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"README.md": "index.html,favicon.ico,robots.txt,CHANGELOG.md",
|
||||
"docker.md": "Dockerfile,docker*.md,nginx*,.dockerignore",
|
||||
"pages.config.ts": "manifest.config.ts,openapi-ts-request.config.ts",
|
||||
"package.json": "tsconfig.json,pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
|
||||
"eslint.config.mjs": ".commitlintrc.*,.prettier*,.editorconfig,.commitlint.cjs,.eslint*"
|
||||
},
|
||||
|
||||
// Disable the default formatter, use eslint instead
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{ "rule": "style/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "format/*", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-indent", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spacing", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-spaces", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-order", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-dangle", "severity": "off", "fixable": true },
|
||||
{ "rule": "*-newline", "severity": "off", "fixable": true },
|
||||
{ "rule": "*quotes", "severity": "off", "fixable": true },
|
||||
{ "rule": "*semi", "severity": "off", "fixable": true }
|
||||
],
|
||||
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"xml",
|
||||
"gql",
|
||||
"graphql",
|
||||
"astro",
|
||||
"svelte",
|
||||
"css",
|
||||
"less",
|
||||
"scss",
|
||||
"pcss",
|
||||
"postcss"
|
||||
],
|
||||
"cSpell.words": [
|
||||
"alova",
|
||||
"Aplipay",
|
||||
"attributify",
|
||||
"chooseavatar",
|
||||
"climblee",
|
||||
"commitlint",
|
||||
"dcloudio",
|
||||
"iconfont",
|
||||
"oxlint",
|
||||
"qrcode",
|
||||
"refresherrefresh",
|
||||
"scrolltolower",
|
||||
"tabbar",
|
||||
"Toutiao",
|
||||
"uniapp",
|
||||
"unibest",
|
||||
"unocss",
|
||||
"uview",
|
||||
"uvui",
|
||||
"Wechat",
|
||||
"WechatMiniprogram",
|
||||
"Weixin"
|
||||
],
|
||||
"i18n-ally.localesPaths": [
|
||||
"src/locale",
|
||||
"src/pages/i18n"
|
||||
],
|
||||
"i18n-ally.keystyle": "nested"
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
{
|
||||
// Place your unibest 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
|
||||
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
|
||||
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
|
||||
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
|
||||
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
|
||||
// Placeholders with the same ids are connected.
|
||||
// Example:
|
||||
// "Print to console": {
|
||||
// "scope": "javascript,typescript",
|
||||
// "prefix": "log",
|
||||
// "body": [
|
||||
// "console.log('$1');",
|
||||
// "$2"
|
||||
// ],
|
||||
// "description": "Log output to console"
|
||||
// }
|
||||
"Print unibest Vue3 SFC": {
|
||||
"scope": "vue",
|
||||
"prefix": "v3",
|
||||
"body": [
|
||||
"<script lang=\"ts\" setup>",
|
||||
"definePage({",
|
||||
" style: {",
|
||||
" navigationBarTitleText: '$1',",
|
||||
" },",
|
||||
"})",
|
||||
"defineOptions({",
|
||||
" name: '$2',",
|
||||
" options: { ",
|
||||
" virtualHost: true,",
|
||||
" },",
|
||||
"})",
|
||||
"</script>\n",
|
||||
"<template>",
|
||||
" <view class=\"\">$3</view>",
|
||||
"</template>\n",
|
||||
"<style lang=\"scss\" scoped>",
|
||||
"//$4",
|
||||
"</style>\n",
|
||||
],
|
||||
},
|
||||
"Print unibest style": {
|
||||
"scope": "vue",
|
||||
"prefix": "st",
|
||||
"body": [
|
||||
"<style lang=\"scss\" scoped>",
|
||||
"//",
|
||||
"</style>\n"
|
||||
],
|
||||
},
|
||||
"Print unibest script with definePage": {
|
||||
"scope": "vue",
|
||||
"prefix": "sc",
|
||||
"body": [
|
||||
"<script lang=\"ts\" setup>",
|
||||
"definePage({",
|
||||
" style: {",
|
||||
" navigationBarTitleText: '$1',",
|
||||
" },",
|
||||
"})",
|
||||
"defineOptions({",
|
||||
" name: '$2',",
|
||||
" options: { ",
|
||||
" virtualHost: true,",
|
||||
" },",
|
||||
"})",
|
||||
"</script>\n"
|
||||
],
|
||||
},
|
||||
"Print unibest template": {
|
||||
"scope": "vue",
|
||||
"prefix": "te",
|
||||
"body": [
|
||||
"<template>",
|
||||
" <view class=\"\">$1</view>",
|
||||
"</template>\n"
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
<script>
|
||||
import HaloConfig from '@/config/halo.config.js';
|
||||
import HaloAdConfig from '@/config/ad.config.js';
|
||||
|
||||
// app升级检测(搭配:https://ext.dcloud.net.cn/plugin?id=4470 升级中心)
|
||||
import CheckAppUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update';
|
||||
import { CheckWxUpdate } from '@/utils/update.js';
|
||||
export default {
|
||||
globalData: {
|
||||
baseApiUrl: HaloConfig.apiUrl,
|
||||
...HaloConfig,
|
||||
haloAdConfig: HaloAdConfig
|
||||
},
|
||||
onLaunch: function() {
|
||||
console.log('App Launch');
|
||||
|
||||
// #ifdef APP-PLUS
|
||||
CheckAppUpdate();
|
||||
// #endif
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
CheckWxUpdate();
|
||||
uni.$tm.vx.commit('setWxShare', HaloConfig.wxShareConfig);
|
||||
// #endif
|
||||
|
||||
// 监听中间按钮(暂时没有使用)
|
||||
uni.onTabBarMidButtonTap(() => {
|
||||
console.log('点击中间按钮');
|
||||
});
|
||||
|
||||
// 初始化博主信息
|
||||
uni.$tm.vx.actions('blogger/fnGetBlogger');
|
||||
|
||||
// 临时:检查是否有用户,没有的话添加一个默认的用户
|
||||
uni.$tm.vx.actions('user/checkAndSetDefaultUser');
|
||||
|
||||
// 启动检查app的配置是否已经就绪,若未就绪则设置默认的
|
||||
uni.$tm.vx.actions('setting/checkAndSetDefaultAppSettings');
|
||||
},
|
||||
onShow: function() {
|
||||
console.log('App Show');
|
||||
},
|
||||
onHide: function() {
|
||||
console.log('App Hide');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// 基础样式
|
||||
@import './common/styles/app.theme.scss';
|
||||
@import './common/styles/app.base.scss';
|
||||
|
||||
// 引入tmUI2.x样式
|
||||
@import './tm-vuetify/mian.min.css';
|
||||
// 引入tmUI2.x主题包
|
||||
@import './tm-vuetify/scss/theme.css';
|
||||
// 引入tmUI2.x预置图标
|
||||
@import './tm-vuetify/scss/fonts/fontawesome_base64.css';
|
||||
|
||||
// 自定义图标
|
||||
@import './common/icons/halocoloriconfont.css';
|
||||
@import './common/icons/haloiconfont.css';
|
||||
@import './common/icons/mphtmliconfont.css';
|
||||
|
||||
/* #ifndef MP-WEIXIN */
|
||||
@import './common/markdown/markdown.scss'; //引入markdown呈现
|
||||
/* #endif */
|
||||
|
||||
page {
|
||||
// background-color: #f3f5f7;
|
||||
// background-color: #f7f7f7;
|
||||
// background-color: #ffffff;
|
||||
background-color: #fafafa;
|
||||
// background-color: #f4f5f5;
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +0,0 @@
|
||||
# 更新日志
|
||||
|
||||
## 暂无更新记录
|
||||
@@ -1,19 +0,0 @@
|
||||
# Git管理同步配置
|
||||
|
||||
#### 同时推送Gitee和Github
|
||||
找到项目目录下的`.git`文件夹,打开`config`文件,修改相关代码:
|
||||
原始代码:
|
||||
```bash
|
||||
[remote "origin"]
|
||||
url = https://gitee.com/ialley-workshop-open/uni-halo.git
|
||||
fetch = +refs/heads/*:refs/remotes/origin/*
|
||||
```
|
||||
修改后的代码:
|
||||
```bash
|
||||
[remote "github"]
|
||||
url = https://github.com/ialley-workshop-open/uni-halo.git
|
||||
fetch = +refs/heads/*:refs/remotes/github/*
|
||||
[remote "gitee"]
|
||||
url = https://gitee.com/ialley-workshop-open/uni-halo.git
|
||||
fetch = +refs/heads/*:refs/remotes/gitee/*
|
||||
```
|
||||
@@ -1,20 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 RuoYi
|
||||
Copyright (c) 2025 菲鸽
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,110 +1,122 @@
|
||||
<p align="center">
|
||||
<a href="https://uni-halo.925i.cn" target="_blank" rel="noopener noreferrer">
|
||||
<img width="100" src="https://b.925i.cn/uni_halo/uni_halo_logo.png" alt="uni-halo logo" />
|
||||
<img width="100" src="https://uni-halo.925i.cn/logo.png" alt="uni-halo logo" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center"><b>uni-halo</b> 基于Halo一款现代化的开源博客/CMS系统API开发的多端系统,值得一试。</p>
|
||||
<p align="center"><b>【uni-halo v3.0】</b> 基于 Halo2.X API 多端项目,值得一试。</p>
|
||||
|
||||
<p align="center"><b style="font-size: 20px;color: #10B5FB">正式支持 Halo2.X 版本,免费开源</b></p>
|
||||
|
||||
<br />
|
||||
<p align="center">
|
||||
<a href="https://b.925i.cn">作者博客</a>
|
||||
<a href="https://uni-halo.925i.cn">文档地址</a>
|
||||
<a href="https://gitee.com/ialley-workshop-open/uni-halo">Gitee仓库</a>
|
||||
<a href="https://github.com/ialley-workshop-open/uni-halo">Github仓库</a>
|
||||
<a href="https://www.xiaoxiaomo.cn">作者主页</a>
|
||||
<a href="https://uni-halo.925i.cn">官方文档</a>
|
||||
<a href="https://github.com/ialley-workshop-open/uni-halo">仓库地址</a>
|
||||
<a href="https://github.com/ialley-workshop-open/plugin-uni-halo">插件仓库</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
如果您觉得这个项目对您有帮助,可以帮作者买杯饮料鼓励鼓励!
|
||||
如果您觉得这个项目对您有帮助,可以帮作者买杯饮料鼓励鼓励,同时为了项目能够持续发展,可以根据您的喜好支持一下本项目哦,非常感谢您的支持,作者也会更有动力持续维护和更新新的功能哦~
|
||||
|
||||
<table rules="none" align="center" border="0">
|
||||
<tr>
|
||||
<td>
|
||||
<center>
|
||||
<img src="https://uni-halo.925i.cn/qrcode/zfb.png" width="100%" />
|
||||
<br/>
|
||||
<font color="AAAAAA">支付宝打赏</font>
|
||||
</center>
|
||||
</td>
|
||||
<td>
|
||||
<center>
|
||||
<img src="https://uni-halo.925i.cn/qrcode/wx.png" width="100%" />
|
||||
<br/>
|
||||
<font color="AAAAAA">微信打赏</font>
|
||||
</center>
|
||||
</td>
|
||||
<td>
|
||||
<center>
|
||||
<img src="https://uni-halo.925i.cn/qrcode/qq.png" width="100%" />
|
||||
<br/>
|
||||
<font color="AAAAAA">QQ打赏</font>
|
||||
</center>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
| 支付宝 | 微信 | QQ |
|
||||
|:---------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------:|
|
||||
|  |  |  |
|
||||
|
||||
## 一、项目介绍
|
||||
|
||||
基于 Halo 2.x 提供的 API 接口,为微信小程序提供的一套开源的博客应用。
|
||||
|
||||
- 完全免费开源,包括程序源码、插件源码
|
||||
- 页面支持插件配置
|
||||
- 使用最新流行的技术栈
|
||||
- 支持特色功能,恋爱日记
|
||||
- 理论支持编译为 小程序(推荐)、APP
|
||||
|
||||
### 部分截图
|
||||
|
||||
|首页|分类|我的|
|
||||
|:--:|:--:|:--:|
|
||||
||||
|
||||
|
||||
|
||||
## 🌸 快速开始
|
||||
|
||||
详细部署文档请查阅 [uni-halo-doc](https://uni-halo.925i.cn/)
|
||||
### 恋爱日记
|
||||
|
||||
- 1、拉取或下载项目<https://gitee.com/ialley-workshop-open/uni-halo>;
|
||||
- 2、通过hbuilderx 导入项目;
|
||||
- 3、命令行执行 npm i 安装依赖;
|
||||
- 4、配置运行信息,找到项目根目录config目录,将`halo.config.template.js`修改为 `halo.config.js` 并设置相关信息;
|
||||
- 5、点击hbuilderx 工具 右上角预览、或者点击工具栏 运行-内置浏览器运行;
|
||||
- 6、项目发行:
|
||||
- 发行小程序:点击工具栏 发行 -> 小程序-微信
|
||||
- 发行APP: 点击工具栏 发行 -> 原生App-云打包
|
||||
- 在 uni-halo 中,我们为您准备了一个恋爱日记的模块,您可以在其中记录您的恋爱故事,分享给您的朋友们,请扫示例小程序,在线体验恋爱日记的功能。
|
||||
|
||||

|
||||
|
||||
<br/>
|
||||
|
||||
|
||||
## ⭐️ 功能清单
|
||||
### 功能介绍
|
||||
|
||||
### 用户部分
|
||||
1. - [x] 文章分类
|
||||
2. - [x] 文章详情&海报分享
|
||||
3. - [x] 文章归档
|
||||
4. - [x] 文章列表和搜索
|
||||
5. - [x] 数据统计展示
|
||||
6. - [x] 图库
|
||||
7. - [x] 友链
|
||||
8. - [x] 留言板
|
||||
9. - [x] 个人日记
|
||||
- 基础功能:文章列表、文章分类、文章详情、个人图库、友情链接、我的瞬间
|
||||
|
||||
### 管理部分
|
||||
1. - [x] 个人信息修改
|
||||
2. - [x] 个人密码修改
|
||||
3. - [x] 日记管理
|
||||
4. - [x] 文章管理
|
||||
5. - [x] 分类管理
|
||||
6. - [x] 标签管理
|
||||
7. - [x] 附件管理
|
||||
8. - [x] 友链管理
|
||||
9. - [x] 图库管理
|
||||
10. - [x] 评论管理
|
||||
11. - [x] 日志管理
|
||||
12. - [ ] 菜单设置
|
||||
13. - [ ] 系统设置
|
||||
|
||||
## 📃 TODO
|
||||
1. - [ ] 暗黑模式
|
||||
2. - [ ] 国际化
|
||||
- 特色功能:恋爱日记,精心为您设计了秀恩爱的模块,秀出关于你们的故事、情侣甜蜜相册、恋爱记录清单,为您的恋爱保驾护航。
|
||||
|
||||
|
||||
## 🌈 在线体验
|
||||
## 二、配套插件
|
||||
|
||||
- 敬请期待...
|
||||
### 下载地址
|
||||
- 代码仓库:[https://github.com/ialley-workshop-open/plugin-uni-halo](https://github.com/ialley-workshop-open/plugin-uni-halo)
|
||||
- 仓库版本:[https://github.com/ialley-workshop-open/plugin-uni-halo/releases](https://github.com/ialley-workshop-open/plugin-uni-halo/releases)
|
||||
- 应用市场:[https://www.halo.run/store/apps/app-ryemX](https://www.halo.run/store/apps/app-ryemX)
|
||||
|
||||
### 使用方式
|
||||
|
||||
直接下载安装插件,在插件市场安装即可,安装完成后,需要配置插件,配置完成后,即可使用插件提供的功能。
|
||||
|
||||
|
||||
## 🔍 许可证
|
||||
<a href="https://gitee.com/ialley-workshop-open/uni-halo/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
|
||||
uni-halo 使用 MIT 协议开源,请遵守开源协议。
|
||||
## 三、相关文档
|
||||
|
||||
## 🏭 贡献
|
||||
- 作者主页:[https://www.xiaoxiaomo.cn/](https://www.xiaoxiaomo.cn/)
|
||||
- 作者博客:[https://blog.xiaoxiaomo.cn/](https://blog.xiaoxiaomo.cn/)
|
||||
- 官方文档:[https://uni-halo.925i.cn/](https://uni-halo.925i.cn/)
|
||||
- 代码仓库:[https://github.com/ialley-workshop-open/uni-halo](https://github.com/ialley-workshop-open/uni-halo)
|
||||
- 插件地址:[https://github.com/ialley-workshop-open/plugin-uni-halo](https://github.com/ialley-workshop-open/uni-halo)
|
||||
- 应用市场:[https://www.halo.run/store/apps/app-ryemX](https://www.halo.run/store/apps/app-ryemX)
|
||||
|
||||
贡献代码请查看 [代码贡献规范](https://uni-halo.925i.cn/standard/category.html)
|
||||
|
||||
## 🙆♂ Halo
|
||||
- 接口文档地址(内容端):<https://api.halo.run/content-api.html>
|
||||
- 接口文档地址(管理端):<https://api.halo.run/admin-api.html>
|
||||
## 四、使用交流
|
||||
|
||||
- 提交 issues:[Github Issues](https://github.com/ialley-workshop-open/uni-halo/issues)
|
||||
- QQ 交流群:632969367
|
||||
- 微信交流群:由于微信交流群时效性太短,所以有需要的话可以先加入QQ群,再邀请进微信群
|
||||
|
||||
<br/>
|
||||
|
||||
## 🎉 加入我们
|
||||
|
||||
uni-halo 还在持续更新中,本项目欢迎您的参与,共同维护,逐步完善,将项目做得更强。项目采用 AGPL-3.0 开源协议,本着一切免费的原则,原则上不会收取任何费用及版权,可以放心使用。
|
||||
|
||||
如果你想加入我们,可以多提供一些好的建议或者提交 pr,我们将会非常乐意接受您的建议和意见。
|
||||
|
||||
<br/>
|
||||
|
||||
## 💪 贡献者们
|
||||
|
||||
感谢以下所有给 uni-halo 贡献过代码的 [开发者](https://github.com/ialley-workshop-open/uni-halo/graphs/contributors)。
|
||||
|
||||
<a href="https://github.com/ialley-workshop-open/uni-halo/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=ialley-workshop-open/uni-halo" />
|
||||
</a>
|
||||
|
||||
<br/>
|
||||
|
||||
## 🔍 开源许可
|
||||
|
||||
uni-halo 使用 AGPL-3.0 协议开源,请遵守开源协议。
|
||||
|
||||
<br/>
|
||||
|
||||
## 🙋 致谢
|
||||
|
||||
uni-halo 得益于以下优秀的技术支撑,我只是站在巨人的肩膀上捡了几根头发丝,但是非常感谢这些优秀的项目,带给了我们其他开发者和用户非常友好的体验!
|
||||
|
||||
- [Halo:一款好用又强大的开源建站工具](https://halo.run/)
|
||||
- [uni-app:是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码可编译到多个平台](https://uniapp.dcloud.net.cn/)
|
||||
- [tm-vuetify:是一个为 uni-app 平台定制的颜值也非常高的 UI 框架](https://www.jx2d.cn/)
|
||||
- [mp-html:一个强大的小程序富文本组件](https://jin-yufeng.gitee.io/mp-html/#/)
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/**
|
||||
* 附件管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/attachment-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 分页获取附件列表
|
||||
* {
|
||||
* "attachmentType": "ALIOSS" "BAIDUBOS" "HUAWEIOBS" "LOCAL" "MINIO" "QINIUOSS" "SMMS" "TENCENTCOS" "UPOSS",
|
||||
* "keyword": "string"
|
||||
* "mediaType": "string"
|
||||
* "page": "string"
|
||||
* "size": "string"
|
||||
* "sort": "string"
|
||||
* }
|
||||
*/
|
||||
getAttachmentsByPage: (params) => {
|
||||
return HttpHandler.Get('/api/admin/attachments', params, {})
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取所有的附件类型
|
||||
*/
|
||||
getAttachmentsMediaTypes: () => {
|
||||
return HttpHandler.Get('/api/admin/attachments/media_types')
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据附件类型获取所有的附件列表
|
||||
*/
|
||||
getAttachmentsTypes: () => {
|
||||
return HttpHandler.Get('/api/admin/attachments/types')
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据附件Id获取附件详情
|
||||
*/
|
||||
getAttachmentsById: (attachmentId) => {
|
||||
return HttpHandler.Get(`/api/admin/attachments/${attachmentId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传附件-单文件(file)
|
||||
* {
|
||||
* file:文件对象
|
||||
* }
|
||||
*/
|
||||
uploadAttachment: (data) => {
|
||||
return HttpHandler.Upload(`/api/admin/attachments/upload`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 上传附件-多文件(files)
|
||||
* {
|
||||
* files:文件对象集合
|
||||
* }
|
||||
*/
|
||||
uploadAttachments: (data) => {
|
||||
return HttpHandler.Upload(`/api/admin/attachments/uploads`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改一个附件信息
|
||||
*/
|
||||
updateAttachmentById: (attachmentId, name) => {
|
||||
return HttpHandler.Put(`/api/admin/attachments/${attachmentId}`, {
|
||||
name: name
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 批量删除附件(id集合)
|
||||
*/
|
||||
deleteAttachmentByIds: (attachmentIds = []) => {
|
||||
return HttpHandler.Delete(`/api/admin/attachments`, attachmentIds)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除单个附件
|
||||
*/
|
||||
deleteAttachmentById: (attachmentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/attachments/${attachmentId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* 文章分类管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/category-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询所有的文章分类
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* "more": "Boolean" ,// 更多参数(回调)
|
||||
* }
|
||||
*/
|
||||
getCategoryList: (params) => {
|
||||
return HttpHandler.Get('/api/admin/categories', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询所有的文章分类(树形数据)
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* }
|
||||
*/
|
||||
getCategoryListTree: (params) => {
|
||||
return HttpHandler.Get('/api/admin/categories/tree_view', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询文章分类详情
|
||||
* @param {Number} categoryId 分类ID
|
||||
*/
|
||||
getCategoryDetail: (categoryId) => {
|
||||
return HttpHandler.Get(`/api/admin/categories/${categoryId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增文章分类
|
||||
* {
|
||||
* "description": "string",
|
||||
* "id": 0,
|
||||
* "name": "string",
|
||||
* "parentId": 0,
|
||||
* "password": "string",
|
||||
* "priority": 0,
|
||||
* "slug": "string",
|
||||
* "thumbnail": "string"
|
||||
* }
|
||||
*/
|
||||
createCategory: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/categories`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章分类信息
|
||||
* @param {Number} categoryId 分类id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updateCategoryById: (categoryId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/categories/${categoryId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除单个文章分类
|
||||
* @param {Number} categoryId 文章分类id
|
||||
*/
|
||||
deleteCategoryById: (categoryId) => {
|
||||
return HttpHandler.Delete(`/api/admin/categories/${categoryId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/**
|
||||
* 文章评论管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/journal-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询文章评论
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
* }
|
||||
*/
|
||||
getPostsComments: (params) => {
|
||||
return HttpHandler.Get('/api/admin/posts/comments', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 回复文章评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postPostsComments: (data) => {
|
||||
return HttpHandler.Post('/api/admin/posts/comments', data)
|
||||
},
|
||||
/**
|
||||
* 更新文章评论状态
|
||||
* @param {Number} commentId id
|
||||
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updatePostsCommentsStatus: (commentId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/comments/${commentId}/status/${status}`)
|
||||
},
|
||||
/**
|
||||
* 删除文章评论
|
||||
* @param {Number} commentId id
|
||||
*/
|
||||
deletePostsCommentsById: (commentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/posts/comments/${commentId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询页面评论
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
* }
|
||||
*/
|
||||
getSheetsComments: (params) => {
|
||||
return HttpHandler.Get('/api/admin/sheets/comments', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 回复页面评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postSheetsComments: (data) => {
|
||||
return HttpHandler.Post('/api/admin/sheets/comments', data)
|
||||
},
|
||||
/**
|
||||
* 更新页面评论状态
|
||||
* @param {Number} commentId id
|
||||
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updateSheetsCommentsStatus: (commentId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/sheets/comments/${commentId}/status/${status}`)
|
||||
},
|
||||
/**
|
||||
* 删除页面评论
|
||||
* @param {Number} commentId id
|
||||
*/
|
||||
deleteSheetsCommentsById: (commentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/sheets/comments/${commentId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询日记评论
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "status": "" , // 类型 "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
* }
|
||||
*/
|
||||
getJournalsComments: (params) => {
|
||||
return HttpHandler.Get('/api/admin/journals/comments', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 回复日记评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postJournalsComments: (data) => {
|
||||
return HttpHandler.Post('/api/admin/journals/comments', data)
|
||||
},
|
||||
/**
|
||||
* 更新日记评论状态
|
||||
* @param {Number} commentId id
|
||||
* @param {String} status "AUDITING" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updateJournalsCommentsStatus: (commentId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/journals/comments/${commentId}/status/${status}`)
|
||||
},
|
||||
/**
|
||||
* 删除日记评论
|
||||
* @param {Number} commentId id
|
||||
*/
|
||||
deleteJournalsCommentsById: (commentId) => {
|
||||
return HttpHandler.Delete(`/api/admin/journals/comments/${commentId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
* 个人日记管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/journal-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询所有的日记列表
|
||||
* {
|
||||
* "keyword":"", // 关键字
|
||||
* "page": 0, // 分页索引
|
||||
* "size": 10, // 分页大小
|
||||
* "sort": ["",""], // 排序
|
||||
* "type": "" , // 类型 "INTIMATE" "PUBLIC"
|
||||
* }
|
||||
*/
|
||||
getJournals: (params) => {
|
||||
return HttpHandler.Get('/api/admin/journals', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询最近的所有的日记列表
|
||||
* {
|
||||
* "top":number, // 数量
|
||||
* }
|
||||
*/
|
||||
getLatestJournals: (params) => {
|
||||
return HttpHandler.Get('/api/admin/journals/latest', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增个人日记
|
||||
* {
|
||||
* "content": "string",
|
||||
* "keepRaw": true,
|
||||
* "sourceContent": "string",
|
||||
* "type": "INTIMATE",
|
||||
* }
|
||||
*/
|
||||
createJournals: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/journals`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改个人日记信息
|
||||
* @param {Number} journalsId id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updateJournalsById: (journalsId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/journals/${journalsId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除个人日记
|
||||
* @param {Number} journalsId id
|
||||
*/
|
||||
deleteJournalsById: (journalsId) => {
|
||||
return HttpHandler.Delete(`/api/admin/journals/${journalsId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* 友链管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/link-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
// 获取友链列表
|
||||
getLinkList: () => {
|
||||
return HttpHandler.Get('/api/admin/links')
|
||||
},
|
||||
/**
|
||||
* 获取友链详情
|
||||
* @params { Number } linkId 友链Id
|
||||
*/
|
||||
getLinkDetail: (linkId) => {
|
||||
return HttpHandler.Get(`/api/admin/links/${linkId}`)
|
||||
},
|
||||
/**
|
||||
* 新增友链
|
||||
* {
|
||||
* "description": "string",
|
||||
* "logo": "string",
|
||||
* "name": "string",
|
||||
* "priority": 0,
|
||||
* "team": "string",
|
||||
* "url": "string"
|
||||
* }
|
||||
*/
|
||||
addLink: (data) => {
|
||||
return HttpHandler.Post('/api/admin/links', data, {})
|
||||
},
|
||||
/**
|
||||
* 修改友链
|
||||
* {
|
||||
* "description": "string",
|
||||
* "logo": "string",
|
||||
* "name": "string",
|
||||
* "priority": 0,
|
||||
* "team": "string",
|
||||
* "url": "string"
|
||||
* }
|
||||
*/
|
||||
updateLink: (linkId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/links/${linkId}`, data, {})
|
||||
},
|
||||
/**
|
||||
* 删除友链
|
||||
* @params { Number } linkId 友链Id
|
||||
*/
|
||||
deleteLink: (linkId) => {
|
||||
return HttpHandler.Delete(`/api/admin/links/${linkId}`)
|
||||
},
|
||||
|
||||
// 获取友链分组
|
||||
getLinkTeamList: (data) => {
|
||||
return HttpHandler.Get('/api/admin/links/teams')
|
||||
},
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* 日志管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/link-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取日志列表列表
|
||||
* params:{ top: Number}
|
||||
*/
|
||||
getLogsLatestList: (params) => {
|
||||
return HttpHandler.Get('/api/admin/logs/latest', params)
|
||||
},
|
||||
/**
|
||||
* 获取日志列表列表
|
||||
* params:{ page:Number,size:Number, sort:String }
|
||||
*/
|
||||
getLogsListByPage: (params) => {
|
||||
return HttpHandler.Get('/api/admin/logs', params)
|
||||
},
|
||||
/**
|
||||
* 清空日志
|
||||
*/
|
||||
deleteAllLogs: () => {
|
||||
return HttpHandler.Get(`/api/admin/logs/clear`)
|
||||
},
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/**
|
||||
* 图库管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/photo-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询图片列表
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* "more": "Boolean" ,// 更多参数(回调)
|
||||
* }
|
||||
*/
|
||||
getPhotos: (params) => {
|
||||
return HttpHandler.Get('/api/admin/photos', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询最近的图库列表(树形数据)
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* }
|
||||
*/
|
||||
getLatestPhotos: (params) => {
|
||||
return HttpHandler.Get('/api/admin/photos/latest', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询所有的图片分组
|
||||
*/
|
||||
getPhotosTeams: () => {
|
||||
return HttpHandler.Get('/api/admin/photos/teams')
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询图片详情
|
||||
* @param {Number} photoId id
|
||||
*/
|
||||
getPhotosDetail: (photoId) => {
|
||||
return HttpHandler.Get(`/api/admin/photos/${photoId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增图片(单图)
|
||||
*{
|
||||
* "description": "string",
|
||||
* "id": 0,
|
||||
* "location": "string",
|
||||
* "name": "string",
|
||||
* "takeTime": "2019-08-24T14:15:22Z",
|
||||
* "team": "string",
|
||||
* "thumbnail": "string",
|
||||
* "url": "string"
|
||||
*}
|
||||
*/
|
||||
createPhotos: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/photos`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增图片(批量)
|
||||
* {
|
||||
* "description": "string",
|
||||
* "id": 0,
|
||||
* "location": "string",
|
||||
* "name": "string",
|
||||
* "takeTime": "2019-08-24T14:15:22Z",
|
||||
* "team": "string",
|
||||
* "thumbnail": "string",
|
||||
* "url": "string"
|
||||
* }
|
||||
*/
|
||||
createPhotosBatch: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/photos/batch`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改图片信息
|
||||
* @param {Number} photoId id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updatePhotosById: (photoId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/photos/${photoId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除单张图片
|
||||
* @param {Number} photoId id
|
||||
*/
|
||||
deletePhotosById: (photoId) => {
|
||||
return HttpHandler.Delete(`/api/admin/photos/${photoId}`)
|
||||
},
|
||||
/**
|
||||
* 批量删除图片
|
||||
* @param {Number} photoIds id数组
|
||||
*/
|
||||
deletePhotosBatchById: (photoIds) => {
|
||||
return HttpHandler.Delete(`/api/admin/photos/batch`, photoIds)
|
||||
},
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* 文章管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/post-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
/**
|
||||
* 新建和编辑文章字段
|
||||
*/
|
||||
const createOrEditModel = {
|
||||
"categoryIds": [
|
||||
0
|
||||
],
|
||||
"content": "string",
|
||||
"createTime": "2019-08-24T14:15:22Z",
|
||||
"disallowComment": true,
|
||||
"editorType": "MARKDOWN",
|
||||
"keepRaw": true,
|
||||
"metaDescription": "string",
|
||||
"metaKeywords": "string",
|
||||
"metas": [{
|
||||
"key": "string",
|
||||
"postId": 0,
|
||||
"value": "string"
|
||||
}],
|
||||
"originalContent": "string",
|
||||
"password": "string",
|
||||
"slug": "string",
|
||||
"status": "DRAFT",
|
||||
"summary": "string",
|
||||
"tagIds": [
|
||||
0
|
||||
],
|
||||
"template": "string",
|
||||
"thumbnail": "string",
|
||||
"title": "string",
|
||||
"topPriority": 0
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询文章列表
|
||||
* @param {Object} params {
|
||||
* categoryId,keyword,page,size,sort,
|
||||
* status:"DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE",statuses,more
|
||||
* }
|
||||
*/
|
||||
getPostsByPage: (params) => {
|
||||
return HttpHandler.Get('/api/admin/posts', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询最近的文章列表
|
||||
* @param {Object} params {top:Number}
|
||||
*/
|
||||
getLatestPosts: (params) => {
|
||||
return HttpHandler.Get('/api/admin/posts/latest', params)
|
||||
},
|
||||
/**
|
||||
* 根据状态查询文章列表
|
||||
* @param {String} status:"DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE"
|
||||
* @param {Object} params:{ page,size,sort,more }
|
||||
*/
|
||||
getPostsPageByStatus: (status, params) => {
|
||||
return HttpHandler.Get(`/api/admin/posts/status/${status}`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据文章id获取文章
|
||||
* @param {Number} postsId 文章id
|
||||
*/
|
||||
getPostsById: (postsId) => {
|
||||
return HttpHandler.Get(`/api/admin/posts/${postsId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增文章
|
||||
* @param {Object} data 同新增
|
||||
* @param {Boolean} isAutoSave 是否来源于自动保存
|
||||
*/
|
||||
createPosts: (data, isAutoSave = false) => {
|
||||
return HttpHandler.Post(`/api/admin/posts?autoSave=${isAutoSave}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章
|
||||
* @param {Number} postsId id
|
||||
* @param {Object} data 同新增
|
||||
* @param {Boolean} isAutoSave 是否来源于自动保存
|
||||
*/
|
||||
updatePostsById: (postsId, data, isAutoSave = false) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/${postsId}?autoSave=${isAutoSave}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章(草稿)
|
||||
* @param {Number} postsId id
|
||||
* @param {Object} data { content,keepRaw,originalContent }
|
||||
*/
|
||||
updatePostsDraftById: (postsId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/${postsId}/status/draft/content`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章状态
|
||||
* @param {Number} postsId id
|
||||
* @param {String} status "DRAFT" "INTIMATE" "PUBLISHED" "RECYCLE"
|
||||
*/
|
||||
updatePostsDraftById: (postsId, status) => {
|
||||
return HttpHandler.Put(`/api/admin/posts/${postsId}/status/${status}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除文章(批量)
|
||||
* @param {Array} postsIds ids
|
||||
*/
|
||||
deletePostsByIds: (postsIds) => {
|
||||
return HttpHandler.Delete(`/api/admin/posts`, postsIds)
|
||||
},
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* 标签管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/tag-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 查询文章标签列表
|
||||
* {
|
||||
* "sort": ["",""], // 排序
|
||||
* "more": "Boolean" ,// 更多参数(回调)
|
||||
* }
|
||||
*/
|
||||
getTagsList: (params) => {
|
||||
return HttpHandler.Get('/api/admin/tags', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询文章标签详情
|
||||
* @param {Number} tagId id
|
||||
*/
|
||||
getTagsDetail: (tagId) => {
|
||||
return HttpHandler.Get(`/api/admin/tags/${tagId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 新增文章标签
|
||||
* {
|
||||
* "color": "#e23d66", // 颜色选择器
|
||||
* "name": "string",
|
||||
* "slug": "string",
|
||||
* "thumbnail": "string"
|
||||
* }
|
||||
*/
|
||||
createTags: (data) => {
|
||||
return HttpHandler.Post(`/api/admin/tags`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改文章标签信息
|
||||
* @param {Number} tagId id
|
||||
* @param {Object} data 同新增
|
||||
*/
|
||||
updateTagsById: (tagId, data) => {
|
||||
return HttpHandler.Put(`/api/admin/tags/${tagId}`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除文章标签
|
||||
* @param {Number} tagId id
|
||||
*/
|
||||
deleteTagsById: (tagId) => {
|
||||
return HttpHandler.Delete(`/api/admin/tags/${tagId}`)
|
||||
},
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* 登录管理
|
||||
* @see https://api.halo.run/admin-api.html#tag/admin-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
// 登录前检查
|
||||
loginPreCheck: (data) => {
|
||||
return HttpHandler.Post('/api/admin/login/precheck', data, {})
|
||||
},
|
||||
// 登录
|
||||
login: (data) => {
|
||||
return HttpHandler.Post('/api/admin/login', data, {})
|
||||
},
|
||||
// 刷新token
|
||||
refreshToken: (refreshToken) => {
|
||||
return HttpHandler.Post($`/api/admin/refresh/${refreshToken}`, {}, {})
|
||||
},
|
||||
|
||||
// 退出登录
|
||||
logout: () => {
|
||||
return HttpHandler.Post('/api/admin/logout')
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取修改密码的验证码
|
||||
* {
|
||||
* "email": "string",
|
||||
* "username": "string"
|
||||
* }
|
||||
*/
|
||||
getResetPasswordCode: () => {
|
||||
return HttpHandler.Post('/api/admin/password/code')
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
* {
|
||||
* "code": "string",
|
||||
* "email": "string",
|
||||
* "password": "stringst",
|
||||
* "username": "string"
|
||||
* }
|
||||
*/
|
||||
resetPasswordByCode: (data) => {
|
||||
return HttpHandler.Put('/api/admin/password/reset', data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取个人信息(当前登录的管理员)
|
||||
*/
|
||||
getAdminProfile: () => {
|
||||
return HttpHandler.Get('/api/admin/users/profiles')
|
||||
},
|
||||
|
||||
/**
|
||||
* 修改个人信息(当前登录的管理员)
|
||||
* {
|
||||
* "avatar": "string",
|
||||
* "description": "string",
|
||||
* "email": "string",
|
||||
* "nickname": "string",
|
||||
* "password": "stringst",
|
||||
* "username": "string"
|
||||
* }
|
||||
*/
|
||||
updateAdminProfile: (data) => {
|
||||
return HttpHandler.Put('/api/admin/users/profiles', data)
|
||||
},
|
||||
/**
|
||||
* 修改密码
|
||||
* {
|
||||
* "confirmPassword": "string",
|
||||
* "newPassword": "string",
|
||||
* "oldPassword": "strings"
|
||||
* }
|
||||
*/
|
||||
modifyAdminPassword: (data) => {
|
||||
return HttpHandler.Put('/api/admin/users/profiles/password', data)
|
||||
},
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 归档接口
|
||||
* @see https://api.halo.run/content-api.html#tag/archive-controlle
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取归档列表(按月)
|
||||
*/
|
||||
getMonthArchives: () => {
|
||||
return HttpHandler.Get(`/api/content/archives/months`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取归档列表(按年)
|
||||
*/
|
||||
getYearArchives: () => {
|
||||
return HttpHandler.Get(`/api/content/archives/years`)
|
||||
},
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* 文章接口
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
export default {
|
||||
/**
|
||||
* 获取文章列表
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getArticleList: (params) => {
|
||||
return HttpHandler.Get('/api/content/posts', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取文章详情
|
||||
* @param {String} articleId 文章id
|
||||
*/
|
||||
getArticleDetail: (articleId) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${articleId}`, {
|
||||
formatDisabled: false,
|
||||
sourceDisabled: true
|
||||
})
|
||||
},
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/**
|
||||
* 博主信息
|
||||
* @see https://api.halo.run/content-api.html#tag/user-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取博主信息
|
||||
*/
|
||||
getBloggerInfo: () => {
|
||||
return HttpHandler.Get(`/api/content/users/profile`)
|
||||
},
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 分类接口
|
||||
* @see https://api.halo.run/content-api.html#tag/category-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
export default {
|
||||
/**
|
||||
* 查询分类列表
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getCategoryList: (params) => {
|
||||
return HttpHandler.Get('/api/content/categories', params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 查询分类下的文章
|
||||
* @param {String} slug 分类名称
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getCategoryPostList: (slug, params) => {
|
||||
return HttpHandler.Get(`/api/content/categories/${slug}/posts`, params)
|
||||
},
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/**
|
||||
* 评论接口
|
||||
* @see https://api.halo.run/content-api.html#tag/post-controller
|
||||
*/
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取评论列表接口(树形数据)
|
||||
* @param {String} postId 文章id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostCommentTree: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/tree_view`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取评论列表接口(列表数据)
|
||||
* @param {String} postId 文章id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostCommentList: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/list_view`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取置顶评论
|
||||
* @param {String} postId 文章id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostTopCommentList: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/top_view`, params)
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 获取评论的子评论列表
|
||||
* @param {String} postId 文章id
|
||||
* @param {String} commentParentId 要获取的评论id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getPostChildrenCommentList: (postId, commentParentId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/comments/${commentParentId}/children`, params)
|
||||
},
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* 功能:全局API管理
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 19:14:44
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
import archive from './archive.js'
|
||||
import article from './article.js'
|
||||
import blogger from './blogger.js'
|
||||
import category from './category.js'
|
||||
import comment from './comment.js'
|
||||
import journal from './journal.js'
|
||||
import link from './link.js'
|
||||
import menu from './menu.js'
|
||||
import option from './option.js'
|
||||
import photo from './photo.js'
|
||||
import post from './post.js'
|
||||
import sheet from './sheet.js'
|
||||
import statistics from './statistics.js'
|
||||
import theme from './theme.js'
|
||||
|
||||
// 管理端
|
||||
import admin_login from './admin/user.js'
|
||||
import admin_links from './admin/links.js'
|
||||
import admin_attachment from './admin/attachment.js'
|
||||
import admin_category from './admin/category.js'
|
||||
import admin_journal from './admin/journal.js'
|
||||
import admin_photos from './admin/photos.js'
|
||||
import admin_tags from './admin/tags.js'
|
||||
import admin_comments from './admin/comments.js'
|
||||
import admin_posts from './admin/posts.js'
|
||||
import admin_logs from './admin/logs.js'
|
||||
|
||||
const ApiManager = {
|
||||
...archive,
|
||||
...article,
|
||||
...blogger,
|
||||
...category,
|
||||
...comment,
|
||||
...journal,
|
||||
...link,
|
||||
...option,
|
||||
...photo,
|
||||
...post,
|
||||
...sheet,
|
||||
...statistics,
|
||||
...theme,
|
||||
// 管理端的api
|
||||
admin: {
|
||||
...admin_login,
|
||||
...admin_links,
|
||||
...admin_attachment,
|
||||
...admin_category,
|
||||
...admin_journal,
|
||||
...admin_photos,
|
||||
...admin_tags,
|
||||
...admin_comments,
|
||||
...admin_posts,
|
||||
...admin_logs
|
||||
}
|
||||
};
|
||||
|
||||
const install = (Vue) => {
|
||||
Vue.prototype.$httpApi = ApiManager
|
||||
}
|
||||
|
||||
export default {
|
||||
install
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* 日记接口
|
||||
* @see https://api.halo.run/content-api.html#tag/journal-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取日记列表
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournals: () => {
|
||||
return HttpHandler.Get(`/api/content/journals`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记详情
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalDetail: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记置顶评论列表
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalTopComments: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/top_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记评论列表(列表形式)
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalCommentList: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/list_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记评论列表(树形式)
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
getJournalCommentTree: (journalId) => {
|
||||
return HttpHandler.Get(`/api/content/journals/${journalId}/comments/tree_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取日记评论列表(树形式)
|
||||
* @param {String} journalId 日记id
|
||||
* @param {String} commentParentId 评论id
|
||||
*/
|
||||
getJournalCommentChildren: (journalId, commentParentId) => {
|
||||
return HttpHandler.Get(
|
||||
`/api/content/journals/${journalId}/comments/${commentParentId}/children`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 发表日记评论
|
||||
* @param {Object} data 评论数据
|
||||
*/
|
||||
postJournalComments: (data) => {
|
||||
return HttpHandler.Post(`/api/content/journals/comments`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 给日记点赞
|
||||
* @param {String} journalId 日记id
|
||||
*/
|
||||
postJournalLikes: (journalId) => {
|
||||
return HttpHandler.Post(`/api/content/journals/${journalId}/likes`)
|
||||
},
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 友链接口
|
||||
* @see https://api.halo.run/content-api.html#tag/link-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取友链列表
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getLinkList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/links`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取分组的友链列表
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getLinkListByTeam: (params) => {
|
||||
return HttpHandler.Get(`/api/content/links/team_view`, params)
|
||||
},
|
||||
}
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
/**
|
||||
* 普通用户登录
|
||||
*/
|
||||
|
||||
// 获取用户信息
|
||||
export function getUserInfo() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.getUserProfile({
|
||||
lang: 'zh_CN',
|
||||
desc: '用户登录', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,
|
||||
success: (res) => {
|
||||
console.log(res, 'resss')
|
||||
resolve(res.userInfo)
|
||||
},
|
||||
fail: (err) => {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function getLogin() {
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.login({
|
||||
success(res) {
|
||||
console.log('----------getLogin ---------')
|
||||
console.log(res)
|
||||
resolve(res)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.log(err, 'logoer')
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function wxLogin() {
|
||||
uni.getProvider({
|
||||
service: 'oauth',
|
||||
success: function(res) {
|
||||
//支持微信、qq和微博等
|
||||
if (~res.provider.indexOf('weixin')) {
|
||||
console.log(res, 'ress')
|
||||
let _userInfo = getUserInfo();
|
||||
let _loginRes = getLogin();
|
||||
Promise.all([_userInfo, _loginRes]).then((res) => {
|
||||
let userInfo = res[0];
|
||||
let loginRes = res[1];
|
||||
if (loginRes.errMsg == 'login:ok') {
|
||||
uni.$tm.vx.commit('user/setWxLoginInfo', {
|
||||
avatarUrl: userInfo.avatarUrl,
|
||||
nickName: userInfo.nickName,
|
||||
email: '',
|
||||
url: ''
|
||||
});
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录成功!'
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: function(err) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function appWxLogin() {
|
||||
uni.login({
|
||||
provider: 'weixin',
|
||||
success: function(loginRes) {
|
||||
// 获取用户信息
|
||||
uni.getUserInfo({
|
||||
provider: 'weixin',
|
||||
success: function(infoRes) {
|
||||
uni.$tm.vx.commit('user/setWxLoginInfo', {
|
||||
avatarUrl: infoRes.userInfo.avatarUrl,
|
||||
nickName: infoRes.userInfo.nickName,
|
||||
email: '',
|
||||
url: ''
|
||||
});
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录成功!'
|
||||
})
|
||||
},
|
||||
fail: function(err) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '登录失败,请重试!'
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* 菜单接口
|
||||
* @see https://api.halo.run/content-api.html#tag/menu-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取菜单列表(列表)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getMenuList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/menus`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取菜单列表(树形)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getMenuTree: (params) => {
|
||||
return HttpHandler.Get(`/api/content/menus/tree_view`, params)
|
||||
},
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* 配置接口
|
||||
* @see https://api.halo.run/content-api.html#tag/option-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 根据key获取配置
|
||||
* @param {String} key 配置的key
|
||||
*/
|
||||
getOptionByKey: (key) => {
|
||||
return HttpHandler.Get(`/api/content/options/keys/${key}`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取配置列表(列表)
|
||||
*/
|
||||
getOptionList: () => {
|
||||
return HttpHandler.Get(`/api/content/options/list_view`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取配置列表(键值对)
|
||||
*/
|
||||
getOptionMap: () => {
|
||||
return HttpHandler.Get(`/api/content/options/map_view`)
|
||||
},
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/**
|
||||
* 图库接口
|
||||
* @see https://api.halo.run/content-api.html#tag/photo-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取图库列表(分页查询)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPhotoListByPage: (params) => {
|
||||
return HttpHandler.Get(`/api/content/photos`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取图库列表(最新)
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPhotoList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/photos/latest`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取图库分组
|
||||
*/
|
||||
getPhotoTeams: () => {
|
||||
return HttpHandler.Get(`/api/content/photos/teams`)
|
||||
},
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* 文章接口
|
||||
* @see https://api.halo.run/content-api.html#tag/post-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取文章列表
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPostList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/posts`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 评论文章
|
||||
* @param {Object} data 数据
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postCommentPost: (data) => {
|
||||
return HttpHandler.Post(`/api/content/posts/comments`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 搜索文章
|
||||
* @param {Object} data 数据
|
||||
*/
|
||||
getPostListByKeyword: (data) => {
|
||||
return HttpHandler.Post(`/api/content/posts/search`, data)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据分类获取文章
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPostDetailBySlug: (params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/slug`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据文章id获取文章
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getPostDetailByPostId: (postId, params) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 给文章点赞
|
||||
* @param {Object} postId 文章id
|
||||
*/
|
||||
postLikePost: (postId) => {
|
||||
return HttpHandler.Post(`/api/content/posts/${postId}/likes`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据当前文章id获取前一篇文章
|
||||
* @param {Object} postId 文章id
|
||||
*/
|
||||
getPrevByCurrentPostId: (postId) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/prev`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据当前文章id获取下一篇文章
|
||||
* @param {Object} postId 文章id
|
||||
*/
|
||||
getNextByCurrentPostId: (postId) => {
|
||||
return HttpHandler.Get(`/api/content/posts/${postId}/next`)
|
||||
},
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/**
|
||||
* 自定义页面模板
|
||||
* @see https://api.halo.run/content-api.html#tag/sheet-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取页面列表
|
||||
* {
|
||||
* page:
|
||||
* size:
|
||||
* sort:
|
||||
* }
|
||||
*/
|
||||
getSheetsList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据分类获取页面数据
|
||||
*/
|
||||
getSheetsListBySlug: (params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/slug`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取页面评论(列表)
|
||||
*/
|
||||
getSheetsCommentsListBySheetId: (sheetId, params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/list_view`, params)
|
||||
},
|
||||
/**
|
||||
* 获取页面评论(树形)
|
||||
*/
|
||||
getSheetsCommentsTreeBySheetId: (sheetId, params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/tree_view`, params)
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 获取评论的子评论列表
|
||||
* @param {String} sheetId 页面id
|
||||
* @param {String} commentParentId 要获取的评论id
|
||||
* @param {Object} params 查询参数
|
||||
*/
|
||||
getSheetsChildrenCommentList: (sheetId, commentParentId, params) => {
|
||||
return HttpHandler.Get(`/api/content/sheets/${sheetId}/comments/${commentParentId}/children`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 给页面添加一个评论
|
||||
* {
|
||||
* "allowNotification": true,
|
||||
* "author": "string",
|
||||
* "authorUrl": "string",
|
||||
* "content": "string",
|
||||
* "email": "string",
|
||||
* "parentId": 0,
|
||||
* "postId": 0
|
||||
* }
|
||||
*/
|
||||
postSheetsComments: (data) => {
|
||||
return HttpHandler.Post(`/api/content/sheets/comments`, data)
|
||||
},
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* 博客统计信息
|
||||
* @see https://api.halo.run/content-api.html#tag/statistic-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取博客统计信息
|
||||
*/
|
||||
getBlogStatistics: () => {
|
||||
return HttpHandler.Get(`/api/content/statistics`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取博客统计信息和用户信息
|
||||
*/
|
||||
getBlogStatisticsWithUser: () => {
|
||||
return HttpHandler.Get(`/api/content/statistics/user`)
|
||||
},
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/**
|
||||
* 主题设置
|
||||
* @see https://api.halo.run/content-api.html#tag/theme-controller
|
||||
*/
|
||||
|
||||
import HttpHandler from '@/common/http/request.js'
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 获取激活主题的信息
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
geActivationThemeList: (params) => {
|
||||
return HttpHandler.Get(`/api/content/themes/activation`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取激活的主题设置
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getActivationThemeSettings: (params) => {
|
||||
return HttpHandler.Get(`/api/content/themes/activation/settings`, params)
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据主题ID列出主题设置
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getThemeSettingsByThemeId: (themeId) => {
|
||||
return HttpHandler.Get(`/api/content/themes/${themeId}/settings`)
|
||||
},
|
||||
|
||||
/**
|
||||
* 通过主题ID获取主题属性
|
||||
* @param {Object} params 参数
|
||||
*/
|
||||
getThemePropertyByThemeId: (themeId) => {
|
||||
return HttpHandler.Get(`/api/content/themes/${themeId}`)
|
||||
},
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* 功能:全局过滤器
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 17:39:04
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 功能描述:时间格式化,将指定的时间戳(或正常的日期)转换为带格式的日期
|
||||
*
|
||||
* 参数说明:
|
||||
* 1.支持格式化 yyyy年MM月dd日 HH点mm分ss秒 星期w q季
|
||||
* 2.对象形式传入 { d:'2021-06-04',f:'yyyy年' } d是必传项,f可不传(默认yyyy-MM-dd HH:mm:ss)
|
||||
* 使用示例:
|
||||
* 1:<view>{{ dateTimeParamName | formatTime }}</view>
|
||||
* 2:<view>{{ { d: '2021-06-04', f: 'yyyy' } | formatTime }}</view>
|
||||
* 3:<view>{{ { d: dateTimeParamName, f: 'yyyy年MM月dd日 HH点mm分ss秒 星期w q季' } | formatTime }}</view>
|
||||
* 特别说明: 由于uniapp中的filter 不支持多参数,但是允许传入对象的形式,故以此方式实现!
|
||||
*/
|
||||
formatTime: function(data) {
|
||||
let _dateTime = new Date(data);
|
||||
let _fmt = 'yyyy-MM-dd HH:mm:ss';
|
||||
if (_dateTime == 'Invalid Date') {
|
||||
if (data.d == undefined || data.d == null || data.d == "") {
|
||||
console.error('日期参数不正确,传入的参数列表:', data);
|
||||
return ''
|
||||
};
|
||||
_dateTime = new Date(data.d);
|
||||
if (_dateTime == 'Invalid Date') {
|
||||
console.error('日期参数不正确,传入的参数列表:', data);
|
||||
return '111'
|
||||
}
|
||||
if (data.hasOwnProperty('f')) {
|
||||
_fmt = data.f
|
||||
}
|
||||
}
|
||||
const _weekDays = ["日", "一", "二", "三", "四", "五", "六"];
|
||||
const _seasons = ["冬", "春", "夏", "秋"];
|
||||
const o = {
|
||||
"M+": _dateTime.getMonth() + 1, //月份
|
||||
"d+": _dateTime.getDate(), //日
|
||||
"H+": _dateTime.getHours(), //小时
|
||||
"m+": _dateTime.getMinutes(), //分
|
||||
"s+": _dateTime.getSeconds(), //秒
|
||||
"w+": _weekDays[_dateTime.getDay()], // 星期几
|
||||
"q+": _seasons[Math.floor((_dateTime.getMonth() + 3) / 3)], //季度
|
||||
S: _dateTime.getMilliseconds(), //毫秒
|
||||
};
|
||||
if (/(y+)/.test(_fmt)) {
|
||||
_fmt = _fmt.replace(
|
||||
RegExp.$1,
|
||||
(_dateTime.getFullYear() + "").substr(4 - RegExp.$1.length)
|
||||
);
|
||||
}
|
||||
for (let k in o) {
|
||||
if (new RegExp("(" + k + ")").test(_fmt)) {
|
||||
_fmt = _fmt.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length == 1 ?
|
||||
o[k] :
|
||||
("00" + o[k]).substr(("" + o[k]).length)
|
||||
);
|
||||
}
|
||||
}
|
||||
return _fmt;
|
||||
},
|
||||
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
/**
|
||||
* 功能:请求工具
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 18:58:03
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
|
||||
import HaloConfig from '@/config/halo.config.js'
|
||||
import {
|
||||
setInterceptors
|
||||
} from "./interceptors.js";
|
||||
import Request from "@/js_sdk/luch-request/luch-request";
|
||||
|
||||
const http = new Request()
|
||||
/* 设置全局配置 */
|
||||
http.setConfig((config) => {
|
||||
|
||||
// 如果是在外部浏览器调试或者编译为h5,请注释该行代码
|
||||
config.baseURL = HaloConfig.apiUrl;
|
||||
|
||||
config.header = {
|
||||
...config.header,
|
||||
'api-authorization': HaloConfig.apiAuthorization,
|
||||
ContentType: 'application/json',
|
||||
dataType: 'json'
|
||||
}
|
||||
return config
|
||||
})
|
||||
setInterceptors(http)
|
||||
|
||||
export {
|
||||
http
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* 功能:http拦截
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 19:02:14
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
import {
|
||||
getAdminAccessToken
|
||||
} from "@/utils/auth.js";
|
||||
import {
|
||||
delCache
|
||||
} from "@/utils/storage";
|
||||
export const setInterceptors = (http) => {
|
||||
http.interceptors.request.use(
|
||||
(config) => {
|
||||
// 可使用async await 做异步操作
|
||||
config.header = {
|
||||
...config.header
|
||||
// ... 可以直接加参数
|
||||
};
|
||||
if (getAdminAccessToken()) {
|
||||
config.header['admin-authorization'] = getAdminAccessToken()
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(config) => {
|
||||
// 可使用async await 做异步操作
|
||||
return Promise.reject(config);
|
||||
}
|
||||
);
|
||||
|
||||
http.interceptors.response.use(
|
||||
(response) => {
|
||||
/* 对响应成功做点什么 可使用async await 做异步操作*/
|
||||
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
|
||||
// return Promise.reject(response) // return Promise.reject 可使promise状态进入catch
|
||||
// if (response.config.custom.verification) { // 演示自定义参数的作用
|
||||
// return response.data
|
||||
// }
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return response.data;
|
||||
} else {
|
||||
return Promise.reject(response);
|
||||
}
|
||||
},
|
||||
(response) => {
|
||||
/* 对响应错误做点什么 (statusCode !== 200)*/
|
||||
if (!response.data) {
|
||||
return Promise.reject({
|
||||
status: 500,
|
||||
message: 'API接口服务异常!'
|
||||
})
|
||||
} else if (response.data.status == 401) {
|
||||
delCache('APP_ADMIN_LOGIN_TOKEN');
|
||||
uni.$eShowModal({
|
||||
title: '提示',
|
||||
content: '您未登录超管账号或登录已过期,是否重新登录?',
|
||||
showCancel: true,
|
||||
cancelText: '否',
|
||||
cancelColor: '#999999',
|
||||
confirmText: '是',
|
||||
confirmColor: '#03a9f4'
|
||||
}).then(res => {
|
||||
uni.navigateTo({
|
||||
url: '/pagesB/login/login'
|
||||
})
|
||||
}).catch(err => {
|
||||
uni.switchTab({
|
||||
url: '/pages/tabbar/about/about'
|
||||
})
|
||||
})
|
||||
} else {
|
||||
return Promise.reject(response.data);
|
||||
}
|
||||
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
/**
|
||||
* 封装各种请求方式
|
||||
*/
|
||||
|
||||
|
||||
import {
|
||||
http
|
||||
} from '@/common/http/index.js'
|
||||
|
||||
export default {
|
||||
Get: (url, params, config = {}) => {
|
||||
return http.get(url, {
|
||||
params,
|
||||
...config
|
||||
})
|
||||
},
|
||||
Post: (url, data, config = {}) => {
|
||||
return http.post(url, data, config)
|
||||
},
|
||||
Put: (url, data, config = {}) => {
|
||||
return http.put(url, data, config)
|
||||
},
|
||||
Upload: (url, config = {}) => {
|
||||
return http.upload(url, config)
|
||||
},
|
||||
Delete: (url, data, config = {}) => {
|
||||
return http.delete(url, data, config)
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* 可以以页面为单位来写,
|
||||
* 比如首页的内容,写在index字段,个人中心写在center,共同部分写在common部分
|
||||
* */
|
||||
|
||||
export default {
|
||||
app: {
|
||||
name: "alley studio",
|
||||
author: "Evan Mo",
|
||||
},
|
||||
tabbar: {
|
||||
home: "Home",
|
||||
moments: "Moments",
|
||||
publish: "Publish",
|
||||
mall: "Mall",
|
||||
mine: "Mine",
|
||||
},
|
||||
// 提示文本
|
||||
tips: {
|
||||
switchLang: "switch Lang",
|
||||
},
|
||||
};
|
||||
@@ -1,23 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* 可以以页面为单位来写,
|
||||
* 比如首页的内容,写在index字段,个人中心写在center,共同部分写在common部分
|
||||
* */
|
||||
|
||||
export default {
|
||||
app: {
|
||||
name: "巷子工坊",
|
||||
author: "小莫唐尼",
|
||||
},
|
||||
tabbar: {
|
||||
home: "首页",
|
||||
moments: "动态",
|
||||
publish: "发布",
|
||||
mall: "商店",
|
||||
mine: "我的",
|
||||
},
|
||||
// 提示文本
|
||||
tips: {
|
||||
switchLang: "切换语言",
|
||||
},
|
||||
};
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* markdown配置
|
||||
*/
|
||||
|
||||
import HaloConfig from '@/config/halo.config.js'
|
||||
export default {
|
||||
domain: HaloConfig.apiUrl,
|
||||
tagStyle: {
|
||||
table: `
|
||||
table-layout: fixed;
|
||||
border-collapse:collapse;
|
||||
margin-bottom: 18px;
|
||||
overflow: hidden;
|
||||
font-size: 13px;
|
||||
color: var(--routine);
|
||||
background: #f2f6fc;
|
||||
border: 1px solid #dcdcdc;
|
||||
border-radius: 4px;
|
||||
`,
|
||||
th: `
|
||||
padding: 8px;
|
||||
border-right: 1px solid var(--classE);
|
||||
border-bottom: 1px solid var(--classE);
|
||||
`,
|
||||
td: `
|
||||
padding: 8px;
|
||||
border-right: 1px solid var(--classE);
|
||||
border-bottom: 1px solid var(--classE);
|
||||
`,
|
||||
blockquote: `
|
||||
padding: 8px 15px;
|
||||
color: #606266;
|
||||
background: #f2f6fc;
|
||||
border-left: 5px solid #50bfff;
|
||||
border-radius: 4px;
|
||||
line-height: 26px;
|
||||
margin-bottom: 18px;
|
||||
`,
|
||||
ul: 'padding-left: 15px;line-height: 1.85;',
|
||||
ol: 'padding-left: 15px;line-height: 1.85;',
|
||||
li: 'margin-bottom: 12px;line-height: 1.85;',
|
||||
h1: `
|
||||
margin: 30px 0 20px;
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
font-size:1.25em;
|
||||
`,
|
||||
h2: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
margin: 22px 0 16px;
|
||||
font-size: 1.2em;
|
||||
`,
|
||||
h3: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
position: relative;
|
||||
margin: 26px 0 18px;
|
||||
font-size: 1.3em;
|
||||
`,
|
||||
h4: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
margin-bottom: 18px;
|
||||
position: relative;
|
||||
font-size: 1.18em;
|
||||
`,
|
||||
h5: `
|
||||
color: var(--main);
|
||||
line-height: 24px;
|
||||
margin-bottom: 14px;
|
||||
position: relative;
|
||||
font-size: 1em;
|
||||
`,
|
||||
h6: `
|
||||
color: #303133;
|
||||
line-height: 24px;
|
||||
margin-bottom: 14px;
|
||||
position: relative;
|
||||
font-size: 1em;
|
||||
`,
|
||||
p: `
|
||||
line-height: 1.65;
|
||||
margin-bottom: 14px;
|
||||
font-size: 15px;
|
||||
`,
|
||||
'code': ` `,
|
||||
strong: 'font-weight: 700;color: rgb(248, 57, 41);',
|
||||
video: 'width: 100%',
|
||||
},
|
||||
containStyle: 'font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;padding:12px;font-size: 16px;color: #606266;word-spacing: 0.8px;letter-spacing: 0.8px;border-radius: 6px;background-color:#FFFFFF;',
|
||||
|
||||
loadingGif: HaloConfig.loadingGifUrl,
|
||||
emptyGif: HaloConfig.loadingEmptyUrl,
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
:root {
|
||||
--main: #303133;
|
||||
--theme: #fb6c28;
|
||||
--code-background: #e8f3ff;
|
||||
--radius-inner: 4px;
|
||||
--classA: #dcdfe6;
|
||||
--classB: #e4e7ed;
|
||||
--classC: #ebeef5;
|
||||
--classD: #f2f6fc;
|
||||
--classE: #dcdcdc;
|
||||
--classF: #333;
|
||||
--classG: #dcdcdc;
|
||||
--classH: #e9f2ff;
|
||||
--classI: #5a3713;
|
||||
--classJ: #f9e5fb;
|
||||
--classK: #e4e7ed;
|
||||
--classL: #666;
|
||||
--classM: #2d2e37;
|
||||
--quote: #50bfff;
|
||||
--code: #409eff;
|
||||
}
|
||||
.evan-markdown {
|
||||
::v-deep {
|
||||
h1::before,
|
||||
h2::before,
|
||||
h3::before,
|
||||
h4::before,
|
||||
h5::before,
|
||||
h6::before {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
content: '';
|
||||
margin-right: 6px;
|
||||
background-position: center;
|
||||
}
|
||||
h1::before {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
content: '¶';
|
||||
top: -4px;
|
||||
margin-right: 12px;
|
||||
font-size: 24px;
|
||||
color: var(--theme);
|
||||
}
|
||||
h2::before {
|
||||
top: -2px;
|
||||
left: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-size: auto 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAVJJREFUWEftl7FKw1AUhv9rbmf1PRw6utkHEOoi6WPoE1ifQJ8gySKCOtRFRBKsDuIiDdZAURBFF50M2sFIzpGogVK03JA2cbh3S/i55zvfvYETgRyLj5erIKOO949FyKm5eHvvHEBV2jyruq1QDQ7n2DNXALEx+D7e2vl6lBYp76scHCzErtmCEPVhqEIAfus8BZk4wM+Zd/46tskDeI0mgLUyAdoAFsoDcM0XCDFdHoDX4FGfbRF3QANoA9qANqANaAPawD82EEWId1sAOJQWz6iO+5nGch41kDw9I3bbYOaTis21wgG4G4AuAzCwXrEoGV6V1ngMJPoPjoB+PzSIa8KBr1QdwFgA6KID7t1AgFYNC5uqxZNcbgB+eASdngHM+9LmpSzF8wFEEagbgHvXoQA3s3aegmYz8P1f4NNVME+39z5e3w4lkSMc3GXtPM1/AjYDFjDGddN5AAAAAElFTkSuQmCC);
|
||||
}
|
||||
h3::before {
|
||||
top: -3px;
|
||||
left: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-size: auto 100%;
|
||||
background-repeat: none;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAH1JREFUWEft1qENgDAQheH/JANg60DAJIgmrMFCzEEYgTFgDyRFIClp0yBf9evl8pl3RsYbp7BjNLHoMptljPiMZH3WAhKQgAQkIIGXQAXUT79cniHaNMa5draliqqojIID86nRHEtvbSqlBSQgAQlIQAISkECRAA746SC5Ad6XpiGnnOGPAAAAAElFTkSuQmCC);
|
||||
}
|
||||
h4::before {
|
||||
top: -2px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
color: var(--theme);
|
||||
background-size: auto 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAndJREFUWEftVkty2kAQfT1SBLtwg6AqwzbxCQIniG8QvAmwCp8kRqvgFa5KwHjlkI3NCeIbhJwgXptUgW9AdiDQdGoITqUsjcZyuYqNZ6WSXs88vel+3YQtL9ry+Xgk8KhApALNAe8x5PPbCUrgabdsD2/e+1edEpN4FsIRps7OwT9cXKKHCLw75QIL+V0XRFIUP1dptBx3ChKkxQlw8UnOG5mqLESgdsYZy5dTAE8jg5lH3Ypd5Mlxxl8ttDgGRulcq5iYgApofgn6ILzVBQsSu5/e0OXiqnMOotdatVjsOvkPl4muQIFrp5y1hJzEBA67ZavEk07WX5EexzxM5b1SYgJrFQbBOQDt3wVSuP0qTRfjowsAr3SHODa75HrqqiKX1gdMycjAYa9stU3JCPBhKue1ExNQAY3BakSgl1HBDMykI9z+Ps0WV0eXIITKdhM3c+yUS259FrVPrBM2TlclEnSmY8+S93tV+/yvH+hxxLzv5D11paFltOLmIFD3FzIbtRMzpr2K5arn+bgzJZAWl8631rjby0zAUJI3xnTfkjQTiFEAwHW3bGWNCoCv0zlvjUukgOoJgPymzQGg3itbfVMOMKiezh30ExOIqwIAvwNHZFUVzMcdbbUonGOnsomr4P1XfiFZ/tS6GOOkW7FqJh8gwomz06ol9oE7O6GhH9zLCbfeCxqDoE3AR61sm5nA/3XUZ47pmneYCSLnAeHLCQGZaAvmH72yXdjMA6oTanHpnFfQ5tDmQ+KJ6MZ+jckXY7//k4pWYB7sQVCUccxU3a9teHKcWS7ne0wI4Rhipqv7REZkku8hvhut+CEOidvjkcDWFfgD9RMzMKE7f80AAAAASUVORK5CYII=);
|
||||
}
|
||||
h5::before {
|
||||
top: -1px;
|
||||
left: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-size: 100% 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAC8klEQVRYR+3WP2gTURwH8O/vKnVRRHKXP52cBO3g4p/BqYNIhy4muajUQRBFKjQV1En6ZxJBcmlRCoJDRe3FDiJVwamLS3FwqbgIgss1l2ZQF5XeT3I1Z3NJ7u5dLlAwN9699/t97vfe7/EIO/yhHe5DD9jpCv3fFVTu856+Xzi62Y/35hj9CFPNrlUwWeRJAJfBGADwBcBNI0/PRZFdAdo4xlQLjCqKjBzogat7hZCRAgPghJGBgbFnxglAOvS7b/fLb+q+qnv5BHBCyEBAWTdnANy2IxOtWSC1qsY+1jOFwAVG+gIVvXyHQbcaKuZCxmf5iMQogXFQtEsBeO5JT6BcMu+Bcb1lUhcyqfExACUAB6JEtgUqi+U5JrrmmcyFHCjwSYtQO+tSUSFbAmXdnAdwJVASdyWLPAS2kbFA8xsHNS13EzCmlx8R6KJQcBcypfFpho3cKxRna3ADsgEo6+ZjAKMhgjZ1d2KWR2gTSyD0h4jnIB2gXDIXwciFCPZvSnMl0wwshYrJGDImaMUGKovlLBPVOrDjh8APzFx8zDkjNa7FzYoGZsKT9XEarQOHmei1aJA246cqOWW6/i2l8VMGzoWIPW/k6eq2Ja6UwCz8pw2JGZ8sS8pUz8fWau/jGp+SgLchcGBgZD1Py41NUuoI+ZloV8ZU93+ogZKzfBiWfXAPCgMJU8Y42avQdMzI4ZBfmSizocqrUeJaAmsvBZGGBCtTziXeRY1rCxRAViEhXckqK93AeQJ9kYTvlmVlqmcTdhNEtefc+9X3utVmuX+CkK6oyqtu4nwrWP8bF5IZdGYjJ79wDuMCz4D+XmhFWnZbt7ab5ltBB6mbkyAaBPFCJassuwMmNb4L4EZgXwBc4AoGTZrUeA6A9x1yK6tzzvnFDlxBv0D176kCP2TCpbbjBXCRV9DZk0VeAONCE1IQ1zWg3dlF1sFQHWQIXFeBNrLAw5BwHBZWjQl6E3SbbB8X+R4Mg/Ca0wN2WtFeBTut4B84mFI4VpekyAAAAABJRU5ErkJggg==);
|
||||
}
|
||||
h6::before {
|
||||
top: -1px;
|
||||
left: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-size: auto 100%;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAEI0lEQVRYR+3Xb2wTZRwH8G/vL22RPy5GW8fK6rJpGeFFY9RO3TRRE1HfmEAammEyjZmOSEg1RrPZaTD6xjhU/Ndlf0CZgwmD+qcgZBRIETeqY0Vcnc7pGonhRY2l3m2t5upqjq693l2vcy+8N81zz/NcPvf75fd7rjos8ku3yH34HyjK0PqlDLPzD56vMNL0VHxmpgXAoUIZXKgI3sMSpK+cNTAbylaj/9IkprnLM3+mkg8XQi4EsJ4lSL+ZNbAfVd+JG/XLcSERg3M8IAtZauBtDEkeNdN6fX9NfRqXueQiSwm00wQRMDMGw94snBJkqYBrSYIIXs/ojftqGq6IXHZRFIpkKYC2JSw9SJE663W0nthlqcMawwrJYpVCag1M48pNy1YNvLWRffGNIYSCv6Cnog7rjCtVIbUEXoG7qeqaNMj51F58dWoKPZY62JeWyUJG+ctcIplcIizWCpgTl9EIyODJn9BtceDWq/6B57tO/H4R688fFaZ7AWzWAiiJEyMDx39Ed6UDdyy7Ni/w1ekxbP95VJi/F8CRYoGycGLksaEJdFU6cPdy0zxkR/RbtE6FhPvHATQUm2JFODHSfyyCLmsd7lth/hf53q/jcE8OC+NTAG7PTKiNoCqcGPnpF+PotDrwwNXl+OC3H9A8cVqYPgPgFnFo1QCLwomRg4cvoPMGBzZHTgq3zwKwZ+ddKVATXAZhf+gdnPvuojAUqmJdrspRAtQU93pXEM+8clgwhQHU5itruUBNcTt6TsP9sl8weQC0S/VFOUBNcW/2folt2z+XhZPTZh6kSKJ79aqVRuFszRxfUm8sNbdz9xlsfekz2bhCQBvDMMM8z+ubN92Mjrb71brS+97dM4wtnk8U4SSBBEGErVarzeVywePxoHVLA1pb6lUh3+8bwZMv+BTjpIAVACa9Xq+uqakJ7e3tqpGd/WfR3Jr+81awIJS0mY0A+sLhMGw2W3qfGmTXvhAef/6gapxUBDuqqqoejUQiBvFbKUH2DHyNx54bLAqXF0jT9HBjY6Pd6/XOi7oc5K7936Dp2QNF4/IBrTqdLuLxeIi2tracRSGF/PDgKB55er8muHzATQB2m81m+P1+1NbmPoVyIfsOnUOj+2PNcPmAO1iWbeE4TmexWODz+WQhayrL4No2oCkuJ5Bl2VGO49ZmcisXObdeVSuRaq7ZZ3G10KBTqRQl3pQPGY1GEQgE4HQ6heVDAO5S1cklNmUDXQRB9KZSqXkfESaTCW63G7FYDKFQKDEyMoJoNKqfe/bbAJ7QGpcrxa8xDLOV5/k0kGXZv2ZnZ5FMJtNjmqYvURR1IpFInAcwAeD7ud/pUuDmAY1G41g8Hl9DkmScoqggx3FHAIyJMMlSQfI9V5zKagDC93dsoRFKimQx2dIWOV/U/yn6bx0WyDj8vgLOAAAAAElFTkSuQmCC);
|
||||
}
|
||||
blockquote > p {
|
||||
margin-bottom: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
font-size: 0.9em !important;
|
||||
}
|
||||
code[class='md-code'],
|
||||
code:not([class]) {
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
color: #409eff;
|
||||
margin: 2px 5px;
|
||||
padding: 0 8px;
|
||||
white-space: normal;
|
||||
text-indent: 0;
|
||||
-webkit-user-select: auto;
|
||||
-moz-user-select: auto;
|
||||
-ms-user-select: auto;
|
||||
user-select: auto;
|
||||
vertical-align: baseline;
|
||||
word-break: break-word;
|
||||
background: #e8f3ff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
code[class*='language-'] {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
// border-radius: 0 0 8px 8px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
user-select: auto;
|
||||
padding: 12px 12px 14px 18px;
|
||||
margin-bottom: 16px;
|
||||
background: #282c34;
|
||||
color: #abb2bf;
|
||||
border-radius: 4px;
|
||||
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||
font-family: 'Fira Code', 'Fira Mono', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
line-height: 1.5;
|
||||
-moz-tab-size: 2;
|
||||
-o-tab-size: 2;
|
||||
tab-size: 2;
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
table {
|
||||
td {
|
||||
padding: 8px;
|
||||
border-right: 1px solid var(--classE);
|
||||
border-bottom: 1px solid var(--classE);
|
||||
}
|
||||
thead th {
|
||||
font-weight: 500;
|
||||
background: var(--classC);
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
transition: background 0.35s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
/**
|
||||
* 功能:全局混入函数
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年07月21日 17:39:32
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
import HaloConfig from '@/config/halo.config.js';
|
||||
import HaloAdConfig from '@/config/ad.config.js';
|
||||
export default {
|
||||
install(Vue) {
|
||||
Vue.mixin({
|
||||
data() {
|
||||
return {
|
||||
author: HaloConfig.author,
|
||||
_isWechat: true,
|
||||
haloAdConfig: HaloAdConfig
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 获取全局应用设置
|
||||
globalAppSettings() {
|
||||
return uni.$tm.vx.getters().setting.getSettings;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this._isWechat = true;
|
||||
uni.$tm.vx.commit('setWxShare', HaloConfig.wxShareConfig);
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
this._isWechat = false;
|
||||
// #endif
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* 设置页面标题
|
||||
* @param {Object} title 标题
|
||||
*/
|
||||
fnSetPageTitle(title) {
|
||||
uni.setNavigationBarTitle({
|
||||
title: title || HaloConfig.title
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面返回顶部
|
||||
*/
|
||||
fnToTopPage(duration = 500) {
|
||||
duration = isNaN(duration) ? 500 : duration
|
||||
uni.pageScrollTo({
|
||||
scrollTop: 0,
|
||||
duration: duration,
|
||||
fail: (err) => {
|
||||
console.log('err:', err);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -1,38 +0,0 @@
|
||||
// 全局css基础样式
|
||||
|
||||
.card-shadow {
|
||||
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
.bg-white {
|
||||
background-color: #fff;
|
||||
}
|
||||
.load-text {
|
||||
padding: 0 0 20rpx 0;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.e-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
/* #ifndef H5 */
|
||||
top: 0;
|
||||
/* #endif */
|
||||
/* #ifdef H5 */
|
||||
top: 88rpx;
|
||||
/* #endif */
|
||||
right: 0;
|
||||
z-index: 6;
|
||||
}
|
||||
.e-loading-icon {
|
||||
animation: eLoading 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes eLoading {
|
||||
0% {
|
||||
transform: rotateZ(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
// 主题
|
||||
|
||||
:root {
|
||||
--theme: #f79ea3;
|
||||
// --theme: #ffaec3;
|
||||
--main: #303133;
|
||||
--main-text-color: rgba(12, 25, 50, 1);
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,304 +0,0 @@
|
||||
<template>
|
||||
<view class="article-card " :class="cardType" @click="fnClickEvent('card')">
|
||||
<view class="left">
|
||||
<cache-image
|
||||
class="thumbnail"
|
||||
radius="12rpx"
|
||||
:url="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
:fileMd5="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
mode="aspectFill"
|
||||
></cache-image>
|
||||
<!-- <image class="thumbnail" lazy-load :src="$utils.checkThumbnailUrl(article.thumbnail)" mode="aspectFill"></image> -->
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="title">
|
||||
<text class="is-top" v-if="article.topped">置顶</text>
|
||||
<text class="title-text text-overflow">{{ article.title }}</text>
|
||||
</view>
|
||||
<view class="content text-overflow-2">{{ article.summary }}</view>
|
||||
<view class="foot">
|
||||
<view class="create-time">
|
||||
<text class="time-label">发布时间:</text>
|
||||
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }}
|
||||
</view>
|
||||
<view class="visits">
|
||||
<!-- <tm-icons :size="24" name="icon-filter-fill"></tm-icons> -->
|
||||
浏览
|
||||
<text class="number">{{ article.visits }}</text>
|
||||
次
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'article-card',
|
||||
props: {
|
||||
from: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
article: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cardType() {
|
||||
// tb_image_text=上图下文
|
||||
// tb_text_image=上文下图
|
||||
if (this.from == 'home' && this.globalAppSettings.layout.home == 'h_row_col2') {
|
||||
if (!['tb_image_text', 'tb_text_image', 'only_text'].some(x => x == this.globalAppSettings.layout.cardType)) {
|
||||
return [this.from, this.globalAppSettings.layout.home, 'tb_image_text'];
|
||||
}
|
||||
return [this.from, this.globalAppSettings.layout.home, this.globalAppSettings.layout.cardType];
|
||||
}
|
||||
return [this.globalAppSettings.layout.home, this.globalAppSettings.layout.cardType];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnClickEvent() {
|
||||
this.$emit('on-click', this.article);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.article-card {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
margin: 0 24rpx;
|
||||
padding: 32rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #ffff;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||
overflow: hidden;
|
||||
margin-bottom: 24rpx;
|
||||
&.home {
|
||||
&.h_row_col2 {
|
||||
margin: 12rpx;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
.thumbnail {
|
||||
::v-deep uni-image {
|
||||
border-radius: 6rpx 6rpx 0 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 26rpx;
|
||||
font-weight: bold;
|
||||
.is-top {
|
||||
height: 36rpx;
|
||||
margin-right: 10rpx;
|
||||
line-height: 36rpx;
|
||||
vertical-align: 4rpx;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
.foot {
|
||||
justify-content: space-between;
|
||||
.create-time {
|
||||
font-size: 24rpx;
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
font-size: 24rpx;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tb_text_image {
|
||||
padding: 12rpx;
|
||||
.left .thumbnail {
|
||||
::v-deep {
|
||||
uni-image {
|
||||
border-radius: 6rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.only_text {
|
||||
padding: 24rpx;
|
||||
.right .foot {
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.lr_image_text {
|
||||
}
|
||||
|
||||
&.lr_text_image {
|
||||
.left {
|
||||
order: 2;
|
||||
padding-left: 30rpx;
|
||||
}
|
||||
.right {
|
||||
order: 1;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
&.tb_image_text {
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
.thumbnail {
|
||||
::v-deep uni-image {
|
||||
border-radius: 6rpx 6rpx 0 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
padding: 24rpx;
|
||||
width: 100%;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tb_text_image {
|
||||
flex-direction: column;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 260rpx;
|
||||
order: 2;
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
order: 1;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.only_text {
|
||||
padding: 36rpx;
|
||||
.left {
|
||||
display: none;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
.content {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
margin-top: 24rpx;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 240rpx;
|
||||
height: 180rpx;
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 30rpx;
|
||||
box-sizing: border-box;
|
||||
.title {
|
||||
display: flex;
|
||||
font-size: 30rpx;
|
||||
color: var(--main-text-color);
|
||||
.is-top {
|
||||
height: 40rpx;
|
||||
padding: 0 12rpx;
|
||||
margin-right: 10rpx;
|
||||
line-height: 40rpx;
|
||||
font-size: 24rpx;
|
||||
white-space: nowrap;
|
||||
vertical-align: 4rpx;
|
||||
color: #fff;
|
||||
background-image: -webkit-linear-gradient(0deg, #3ca5f6 0, #a86af9 100%);
|
||||
border-radius: 4rpx 12rpx;
|
||||
}
|
||||
&-text {
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
display: -webkit-box;
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
height: 80rpx;
|
||||
margin-top: 14rpx;
|
||||
line-height: 42rpx;
|
||||
}
|
||||
.foot {
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #909399;
|
||||
margin-top: 18rpx;
|
||||
|
||||
.create-time {
|
||||
font-size: 26rpx;
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
.number {
|
||||
padding: 0 6rpx;
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,197 +0,0 @@
|
||||
<template>
|
||||
<view class="article-min-card" :class="[globalAppSettings.layout.cardType]" @click="fnClickEvent('card')">
|
||||
<view class="left">
|
||||
<cache-image
|
||||
class="thumbnail"
|
||||
radius="12rpx"
|
||||
:url="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
:fileMd5="$utils.checkThumbnailUrl(article.thumbnail)"
|
||||
mode="aspectFill"
|
||||
></cache-image>
|
||||
</view>
|
||||
<view class="right">
|
||||
<view class="title text-overflow">{{ article.title }}</view>
|
||||
<view class="content text-overflow">{{ article.summary }}</view>
|
||||
<view class="foot">
|
||||
<view class="create-time">
|
||||
<!-- <text class="icon iconfont icon-clock"></text> -->
|
||||
<text class="time-label">发布时间:</text>
|
||||
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }}
|
||||
</view>
|
||||
<view class="visits">
|
||||
<!-- <text class="icon iconfont icon-eye"></text> -->
|
||||
浏览
|
||||
<text class="number">{{ article.visits }}</text>
|
||||
次
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'article-min-card',
|
||||
props: {
|
||||
article: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnClickEvent() {
|
||||
this.$emit('on-click', this.article);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.article-min-card {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
border-radius: 12rpx;
|
||||
background-color: #ffff;
|
||||
overflow: hidden;
|
||||
margin: 12rpx 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
padding: 16rpx;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
&.lr_image_text {
|
||||
}
|
||||
|
||||
&.lr_text_image {
|
||||
.left {
|
||||
order: 2;
|
||||
padding-left: 30rpx;
|
||||
}
|
||||
.right {
|
||||
order: 1;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
&.tb_image_text {
|
||||
flex-direction: column;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 220rpx;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
.title {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.tb_text_image {
|
||||
flex-direction: column;
|
||||
.left {
|
||||
width: 100%;
|
||||
height: 220rpx;
|
||||
order: 2;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
width: 100%;
|
||||
order: 1;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.only_text {
|
||||
.left {
|
||||
display: none;
|
||||
}
|
||||
.right {
|
||||
padding-left: 0;
|
||||
.foot {
|
||||
justify-content: flex-start;
|
||||
.create-time {
|
||||
.time-label {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
width: 180rpx;
|
||||
height: 130rpx;
|
||||
.thumbnail {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
.right {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 24rpx;
|
||||
.title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
.content {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
.foot {
|
||||
display: flex;
|
||||
font-size: 24rpx;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #909399;
|
||||
margin-top: 14rpx;
|
||||
.create-time {
|
||||
font-size: 24rpx;
|
||||
.time-label {
|
||||
display: none;
|
||||
}
|
||||
.icon {
|
||||
font-size: 24rpx;
|
||||
padding-right: 4rpx;
|
||||
}
|
||||
}
|
||||
.visits {
|
||||
.icon {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.number {
|
||||
padding: 0 6rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,207 +0,0 @@
|
||||
<template>
|
||||
<tm-poup v-model="show" position="bottom" height="auto" @change="fnClose">
|
||||
<view class="poup-head pa-24 text-align-center text-weight-b ">{{ title }}</view>
|
||||
<view class="poup-body pa-24 pt-0 pb-0">
|
||||
<view v-if="loading != 'success'" class="loading-wrap flex flex-center">
|
||||
<view v-if="loading == 'loading'" class="loading">加载中...</view>
|
||||
<view v-else class="error" @click="fnGetData()">加载失败,点击刷新!</view>
|
||||
</view>
|
||||
<block v-else>
|
||||
<view v-if="total == 0" class="empty">无附件</view>
|
||||
<scroll-view v-else class="poup-content" :enable-flex="true" :scroll-y="true" @touchmove.stop>
|
||||
<view class="card-content">
|
||||
<view class="card pa-12" v-for="(file, index) in dataList" :key="index" @click="fnOnSelect(file, index)">
|
||||
<view class="card-inner round-3" :class="{ 'is-select': selectIndex == index }">
|
||||
<cache-image v-if="file.isImage" class="cover" height="160rpx" :url="file.thumbPath" :fileMd5="file.thumbPath" mode="aspectFill"></cache-image>
|
||||
<view v-else class="cover flex pl-46 pr-46 flex-center bg-gradient-blue-grey-accent text-align-center text-size-m">{{ file.mediaType }}</view>
|
||||
|
||||
<view class="name text-overflow text-size-m pa-12">{{ file.name }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
</view>
|
||||
<view class="poup-foot pa-30 pb-12 pt-0">
|
||||
<!-- 分页 -->
|
||||
<view v-if="total > queryParams.size" class="mt-36 pl-24 pr-24">
|
||||
<tm-pagination color="bg-gradient-blue-accent" :page.sync="queryParams.page" :total="total" :totalVisible="5" @change="fnGetPagingData"></tm-pagination>
|
||||
</view>
|
||||
<view class=" flex flex-center mt-12">
|
||||
<tm-button size="m" theme="bg-gradient-blue-accent" @click="fnSava()">确定选用</tm-button>
|
||||
<tm-button size="m" theme="bg-gradient-orange-accent" @click="fnUpload()">上传</tm-button>
|
||||
<tm-button size="m" theme="bg-gradient-blue-grey-accent" @click="fnClose(false)">关 闭</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
</tm-poup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAdminAccessToken } from '@/utils/auth.js';
|
||||
|
||||
import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
|
||||
import tmPagination from '@/tm-vuetify/components/tm-pagination/tm-pagination.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
export default {
|
||||
name: 'attachment-select',
|
||||
components: { tmPoup, tmPagination, tmButton },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: '附件列表'
|
||||
},
|
||||
selectType: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: true,
|
||||
loading: 'loading',
|
||||
total: 0,
|
||||
queryParams: {
|
||||
size: 6,
|
||||
page: 1
|
||||
},
|
||||
dataList: [],
|
||||
select: null,
|
||||
selectIndex: -1
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fnGetData();
|
||||
},
|
||||
methods: {
|
||||
fnGetData() {
|
||||
this.queryParams.page = 1;
|
||||
this.fnGetPagingData(1);
|
||||
},
|
||||
fnGetPagingData(page) {
|
||||
this.loading = 'loading';
|
||||
const _params = {
|
||||
...this.queryParams
|
||||
};
|
||||
_params.page = page - 1;
|
||||
this.$httpApi.admin
|
||||
.getAttachmentsByPage(_params)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
this.total = res.data.total;
|
||||
this.dataList = res.data.content.map(file => {
|
||||
if (this.$utils.fnCheckIsFileType('image', file) && file.size / 1024 / 1024 > 2) {
|
||||
file.isImage = false;
|
||||
file.desc = '图片过大无法显示缩略图';
|
||||
} else {
|
||||
file.isImage = this.$utils.fnCheckIsFileType('image', file);
|
||||
}
|
||||
file.thumbPath = this.$utils.checkThumbnailUrl(file.thumbPath);
|
||||
return file;
|
||||
});
|
||||
this.loading = 'success';
|
||||
} else {
|
||||
uni.$tm.toast('加载失败,请重试!');
|
||||
this.loading = 'error';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
uni.$tm.toast('加载失败,请重试!');
|
||||
this.loading = 'error';
|
||||
});
|
||||
},
|
||||
fnOnSelect(file, index) {
|
||||
this.select = file;
|
||||
this.selectIndex = index;
|
||||
},
|
||||
fnSava() {
|
||||
if (this.selectType) {
|
||||
if (this.$utils.fnCheckIsFileType(this.selectType, this.select)) {
|
||||
this.$emit('on-select', this.select);
|
||||
} else {
|
||||
uni.$tm.toast('该附件类型不符合!');
|
||||
}
|
||||
} else {
|
||||
this.$emit('on-select', this.select);
|
||||
}
|
||||
},
|
||||
fnClose(e) {
|
||||
if (!e) {
|
||||
this.$emit('on-close');
|
||||
}
|
||||
},
|
||||
fnUpload() {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
success: res => {
|
||||
uni.uploadFile({
|
||||
filePath: res.tempFilePaths[0],
|
||||
header: {
|
||||
'admin-authorization': getAdminAccessToken()
|
||||
},
|
||||
url: this.$baseApiUrl + '/api/admin/attachments/upload',
|
||||
name: 'file',
|
||||
success: upladRes => {
|
||||
const _uploadRes = JSON.parse(upladRes.data);
|
||||
if (_uploadRes.status == 200) {
|
||||
uni.$tm.toast('上传成功!');
|
||||
this.fnGetData(1);
|
||||
} else {
|
||||
uni.$tm.toast(_uploadRes.message);
|
||||
}
|
||||
},
|
||||
fail: err => {
|
||||
uni.$tm.toast(err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.poup-head {
|
||||
}
|
||||
.poup-body {
|
||||
height: 50vh;
|
||||
}
|
||||
.loading-wrap {
|
||||
height: 50vh;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.poup-content {
|
||||
height: inherit;
|
||||
box-sizing: border-box;
|
||||
.card-content {
|
||||
height: inherit;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
.card {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
&-inner {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
border: 4rpx solid transparent;
|
||||
&.is-select {
|
||||
border-color: rgb(13, 141, 242);
|
||||
}
|
||||
}
|
||||
.cover {
|
||||
width: 100%;
|
||||
height: 160rpx;
|
||||
flex-wrap: wrap;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.name {
|
||||
color: #303133;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,194 +0,0 @@
|
||||
<template>
|
||||
<view class="bottom-tool-bar">
|
||||
<tm-translate :auto="true" animation-name="fadeUp">
|
||||
<view class="content flex">
|
||||
<view class="input" @click="fnToComment()">
|
||||
<text class="icon iconfont icon-edit"></text>
|
||||
<text class="text">(*^▽^*)说点啥吧~</text>
|
||||
</view>
|
||||
<view class="right flex">
|
||||
<!-- 点赞 -->
|
||||
<view class="item likes" @click="fnDoLikes()">
|
||||
<view class="iconfont icon-like"></view>
|
||||
<view class="text">{{ tempPost.likes }}</view>
|
||||
</view>
|
||||
<!-- 评论 -->
|
||||
<view class="item comment">
|
||||
<view class="iconfont icon-comment-dots"></view>
|
||||
<view class="text">{{ tempPost.commentCount }}</view>
|
||||
</view>
|
||||
<!-- 分享 -->
|
||||
<view class="item share" @click="fnOnShare()"><text class="iconfont icon-share1"></text></view>
|
||||
</view>
|
||||
</view>
|
||||
</tm-translate>
|
||||
|
||||
<tm-shareSheet @change="fnOnShareChange" :actions="share.list" title="分享文章" v-model="share.show"></tm-shareSheet>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
|
||||
import tmShareSheet from '@/tm-vuetify/components/tm-shareSheet/tm-shareSheet.vue';
|
||||
export default {
|
||||
name: 'bottom-tool-bar',
|
||||
components: {
|
||||
tmTranslate,
|
||||
tmShareSheet
|
||||
},
|
||||
props: {
|
||||
// 文章数据
|
||||
post: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
// 其他参数
|
||||
params: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
share: {
|
||||
show: false,
|
||||
list: [
|
||||
[
|
||||
{ name: '微信好友', bgcolor: '#07c160', icon: 'icon-weixin', color: 'white' },
|
||||
{ name: '朋友圈', bgcolor: '#04c887', icon: 'icon-pengyouquan', color: 'white' },
|
||||
{ name: '生成海报', bgcolor: '#1dc0fd', icon: 'icon-QQ', color: 'white' }
|
||||
]
|
||||
]
|
||||
},
|
||||
tempPost: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
post: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
console.log('watch', val);
|
||||
this.tempPost = this.$utils.deepClone(val);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log(this.post);
|
||||
this.tempPost = this.$utils.deepClone(this.post);
|
||||
console.log(this.tempPost);
|
||||
},
|
||||
methods: {
|
||||
fnToComment() {
|
||||
this.$Router.push({
|
||||
path: '/pagesA/comment/comment',
|
||||
query: {
|
||||
postId: this.post.id,
|
||||
parentId: 0,
|
||||
title: this.post.title,
|
||||
formPage: 'comment_list',
|
||||
type: 'post'
|
||||
}
|
||||
});
|
||||
},
|
||||
fnDoLikes() {
|
||||
this.$httpApi
|
||||
.postLikePost(this.post.id)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '点赞成功'
|
||||
});
|
||||
this.tempPost.likes += 1;
|
||||
} else {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: res.message
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: err.message
|
||||
});
|
||||
});
|
||||
},
|
||||
fnOnShare() {
|
||||
// this.$emit('on-share');
|
||||
this.share.show = true;
|
||||
},
|
||||
fnOnShareChange(e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bottom-tool-bar {
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 401;
|
||||
|
||||
::v-deep {
|
||||
.tm-shareSheet-wk .uni-scroll-view-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
padding: 24rpx;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0rpx -4rpx 24rpx rgba(0, 0, 0, 0.07);
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
.input {
|
||||
width: 280rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 60rpx;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
.icon {
|
||||
}
|
||||
.text {
|
||||
padding-left: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-left: 24rpx;
|
||||
.item {
|
||||
margin-left: 24rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
&.share {
|
||||
.iconfont {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
}
|
||||
.iconfont {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
}
|
||||
.text {
|
||||
padding-left: 6rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,180 +0,0 @@
|
||||
<template>
|
||||
<view class="">
|
||||
<view v-if="loadStatus == 'loading'" class="img-loading" :style="[imgStyle, loadStyle]">
|
||||
<!-- <text class="img-load-icon iconfont icon-loading"></text>
|
||||
<text class="img-load-text">{{ loadText }}</text> -->
|
||||
<image :src="loadingImgSrc" :style="[imgStyle]" mode="aspectFit"></image>
|
||||
</view>
|
||||
<view v-if="loadStatus == 'error'" class="img-error" :style="[imgStyle, loadErrStyle]">
|
||||
<text class="img-err-icon iconfont icon-exclamation-circle"></text>
|
||||
<text class="img-load-text">{{ loadErrText }}</text>
|
||||
</view>
|
||||
<image
|
||||
v-show="loadStatus == 'success'"
|
||||
:src="src"
|
||||
@load="fnOnLoad"
|
||||
@error="fnOnError"
|
||||
:lazy-load="lazyLoad"
|
||||
:style="[imgStyle]"
|
||||
:mode="mode"
|
||||
@click="$emit('on-click', url)"
|
||||
></image>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import imageCache from '@/utils/imageCache.js';
|
||||
export default {
|
||||
name: 'cache-image',
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
lazyLoad: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
loadStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
backgroundColor: '#ffffff',
|
||||
color: '#333'
|
||||
};
|
||||
}
|
||||
},
|
||||
loadErrStyle: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
color: 'rgba(244, 67, 54,1)'
|
||||
// backgroundColor: 'rgba(244, 67, 54,0.2)'
|
||||
};
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'aspectFill'
|
||||
},
|
||||
loadText: {
|
||||
type: String,
|
||||
default: '加载中...'
|
||||
},
|
||||
loadErrText: {
|
||||
type: String,
|
||||
default: '加载失败'
|
||||
},
|
||||
fileMd5: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
styles: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
radius: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
imgStyle: {},
|
||||
src: '', // 图片地址
|
||||
loadStatus: 'loading'
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
loadingImgSrc() {
|
||||
return getApp().globalData.loadingGifUrl;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听图片md5值的变化
|
||||
fileMd5(val) {
|
||||
// 查找获取图片缓存
|
||||
this.fnGetImageCache();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.imgStyle = {
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
borderRadius: this.radius,
|
||||
...this.styles
|
||||
};
|
||||
|
||||
// 查找获取图片缓存
|
||||
this.fnGetImageCache();
|
||||
},
|
||||
methods: {
|
||||
// 查找获取图片缓存
|
||||
async fnGetImageCache() {
|
||||
// #ifdef APP-PLUS
|
||||
var result = await imageCache.getImageCache(this.url, this.fileMd5);
|
||||
if (result) {
|
||||
this.src = result;
|
||||
} else {
|
||||
this.src = this.url;
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
this.src = this.url;
|
||||
// #endif
|
||||
},
|
||||
fnOnLoad() {
|
||||
this.loadStatus = 'success';
|
||||
},
|
||||
fnOnError() {
|
||||
this.loadStatus = 'error';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.img-loading,
|
||||
.img-error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.img-load-icon {
|
||||
font-size: 36rpx;
|
||||
animation: xhRote 0.8s infinite linear;
|
||||
}
|
||||
.img-load-text {
|
||||
font-size: 28rpx;
|
||||
margin-top: 8rpx;
|
||||
color: inherit;
|
||||
}
|
||||
.img-error {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.img-err-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
@keyframes xhRote {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<view class="category-mini-card">
|
||||
<!-- <image class="img" lazy-load :src="$utils.checkThumbnailUrl(category.thumbnail)" mode="aspectFill"></image> -->
|
||||
<cache-image
|
||||
class="img"
|
||||
height="120rpx"
|
||||
:url="$utils.checkThumbnailUrl(category.thumbnail)"
|
||||
:fileMd5="$utils.checkThumbnailUrl(category.thumbnail)"
|
||||
mode="aspectFill"
|
||||
></cache-image>
|
||||
|
||||
<text class="label">{{ category.postCount }} 篇</text>
|
||||
<view class="name">{{ category.name }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'category-mini-card',
|
||||
props: {
|
||||
category: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.category-mini-card {
|
||||
display: inline-block;
|
||||
width: 260rpx;
|
||||
height: 180rpx;
|
||||
position: relative;
|
||||
border-radius: 12rpx;
|
||||
background-color: #fff;
|
||||
overflow: hidden;
|
||||
// border: 2rpx solid #f7f7f7;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
|
||||
.img {
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
border: 6rpx 6rpx 0 0;
|
||||
}
|
||||
.label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 86rpx;
|
||||
color: #03a9f4;
|
||||
font-size: 24rpx;
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 0rpx 24rpx 0 0;
|
||||
display: flex;
|
||||
padding: 2rpx 12rpx;
|
||||
padding-right: 24rpx;
|
||||
}
|
||||
.name {
|
||||
font-size: 24rpx;
|
||||
text-align: center;
|
||||
color: var(--main-text-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,147 +0,0 @@
|
||||
<template>
|
||||
<view class=" comment-item flex flex-col mt-30 pt-24" :class="{ 'child-comment-item': isChild, 'no-solid': !useSolid, classItem }">
|
||||
<view class="comment-item_user flex">
|
||||
<image v-if="comment.isAdmin" class="user-avatar" :src="bloggerInfo.avatar" mode="aspectFill" @error="fnOnImageError(comment)"></image>
|
||||
<image v-else class="user-avatar" :src="comment.avatar" mode="aspectFill" @error="fnOnImageError(comment)"></image>
|
||||
<view class="user-info pl-14">
|
||||
<view class="author">
|
||||
<text class="mr-6 text-grey-darken-1 text-size-m">{{ comment.author }}</text>
|
||||
<tm-tags v-if="comment.isAdmin" :dense="true" color="bg-gradient-amber-accent" size="xs" model="fill">博主</tm-tags>
|
||||
|
||||
<tm-tags v-else :dense="true" color="bg-gradient-light-blue-lighten " size="xs" model="fill">游客</tm-tags>
|
||||
</view>
|
||||
<view class="flex mt-4">
|
||||
<view v-if="false" class="text-size-s text-grey mr-12">IP属地:浙江省杭州市</view>
|
||||
<view class="time text-size-xs text-grey">
|
||||
<text class="">{{ $tm.dayjs(comment.createTime).format('YYYY年MM月DD日') }}</text>
|
||||
<text class="ml-12">{{ $tm.dayjs(comment.createTime).fromNow(true) }}前</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="useActions" class="">
|
||||
<tm-button size="s" text theme="blue" @click="$emit('on-comment', { type: 'user', comment: comment })">回复</tm-button>
|
||||
<tm-button size="s" text theme="grey" @click="$emit('on-copy', comment.content)">复制</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="comment-item_content mt-12" :class="{ 'has-bg': useContentBg, 'not-ml': isChild }" @click="$emit('on-detail', comment)" v-html="comment.content"></view>
|
||||
|
||||
<!-- <view v-if="useActions" class="comment-item_info text-size-s text-grey">
|
||||
<text v-if="false" @click="$emit('on-todo')">点赞</text>
|
||||
<text @click="$emit('on-comment', { type: 'user', comment: comment })">回复</text>
|
||||
<text v-if="false" class="ml-24" @click="$emit('on-todo')">举报</text>
|
||||
<text class="ml-24" @click="$emit('on-copy', comment.content)">复制内容</text>
|
||||
</view> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
export default {
|
||||
name: 'comment-item',
|
||||
components: { tmTags, tmButton },
|
||||
props: {
|
||||
classItem: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
useActions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useSolid: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useContentBg: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
isChild: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
postId: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
comment: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 获取博主信息
|
||||
bloggerInfo() {
|
||||
let blogger = this.$tm.vx.getters().blogger.getBlogger;
|
||||
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||
return blogger;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnOnImageError(data) {
|
||||
if (data.isAdmin) {
|
||||
data.avatar = this.$haloConfig.author.avatar;
|
||||
} else {
|
||||
data.avatar = `${this.$haloConfig.defaultAvatarUrl}&rt=${new Date().getTime()}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.comment-item {
|
||||
box-sizing: border-box;
|
||||
border-top: 2rpx solid #f5f5f5;
|
||||
|
||||
&.child-comment-item {
|
||||
padding-top: 0;
|
||||
margin-left: 80rpx;
|
||||
border: 0;
|
||||
}
|
||||
&.no-solid {
|
||||
border: 0;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
&_user {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.user-avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
border: 4rpx solid #ffffff;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.user-info {
|
||||
width: 0;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&_content {
|
||||
font-size: 28rpx;
|
||||
margin-left: 80rpx;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10rpx;
|
||||
line-height: 1.8;
|
||||
color: var(--main-text-color);
|
||||
&.has-bg {
|
||||
background-color: #fafafa;
|
||||
padding: 6rpx 24rpx;
|
||||
}
|
||||
&.not-ml {
|
||||
margin-left: 80rpx;
|
||||
}
|
||||
}
|
||||
&_info {
|
||||
margin-top: 6rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 80rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,266 +0,0 @@
|
||||
<template>
|
||||
<view class="comment-list">
|
||||
<!-- 顶部区域 -->
|
||||
<view class="comment-list_head">
|
||||
<view class="title">
|
||||
评论列表
|
||||
<text class="count">({{ result.total || 0 }}条)</text>
|
||||
</view>
|
||||
<view class="filter">
|
||||
<text class="filter-item " :class="{ active: sort == 0 }" @click="fnOnSort(0)">默认</text>
|
||||
<text class="filter-item " :class="{ active: sort == 1 }" @click="fnOnSort(1)">热评</text>
|
||||
<!-- <text class="filter-item">全部</text> -->
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view v-if="disallowComment" class="disallow-comment"><tm-empty icon="icon-shiliangzhinengduixiang-" label="文章已开启禁止评论"></tm-empty></view> -->
|
||||
<!-- 内容区域 -->
|
||||
<view class="comment-list_content">
|
||||
<view v-if="loading != 'success'" class="loading-wrap flex">
|
||||
<view v-if="loading == 'loading'" class="loading flex flex-center flex-col">
|
||||
<text class="e-loading-icon iconfont icon-loading text-blue"></text>
|
||||
<view class="text-size-n text-grey-lighten-1 py-12 mt-12">加载中,请稍等...</view>
|
||||
</view>
|
||||
<view v-else-if="loading == 'error'" class="error">
|
||||
<tm-empty icon="icon-wind-cry" label="加载失败">
|
||||
<tm-button theme="bg-gradient-light-blue-accent" size="m" v-if="!disallowComment" @click="fnToComment()">刷新试试</tm-button>
|
||||
</tm-empty>
|
||||
</view>
|
||||
</view>
|
||||
<block v-else>
|
||||
<view class="empty pt-50" v-if="dataList.length == 0">
|
||||
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无评论">
|
||||
<tm-button theme="bg-gradient-light-blue-accent" size="m" v-if="!disallowComment" @click="fnToComment(null)">抢沙发</tm-button>
|
||||
</tm-empty>
|
||||
</view>
|
||||
<block v-else>
|
||||
<!-- 评论内容 : 目前仅支持二级评论 -->
|
||||
<block v-for="(comment, index) in dataList" :key="comment.id">
|
||||
<comment-item
|
||||
:useContentBg="false"
|
||||
:isChild="false"
|
||||
:comment="comment"
|
||||
:postId="postId"
|
||||
@on-copy="fnCopyContent"
|
||||
@on-comment="fnToComment"
|
||||
@on-todo="fnToDo"
|
||||
@on-detail="fnShowCommetnDetail"
|
||||
></comment-item>
|
||||
|
||||
<!-- 二级评论 -->
|
||||
<block v-if="comment.children && comment.children.length != 0">
|
||||
<block v-for="(childComment, childIndex) in comment.children" :key="childComment.id">
|
||||
<comment-item
|
||||
:useContentBg="false"
|
||||
:isChild="true"
|
||||
:comment="childComment"
|
||||
:postId="postId"
|
||||
@on-copy="fnCopyContent"
|
||||
@on-comment="fnToComment"
|
||||
@on-todo="fnToDo"
|
||||
@on-detail="fnShowCommetnDetail"
|
||||
></comment-item>
|
||||
</block>
|
||||
</block>
|
||||
</block>
|
||||
<view v-if="false" class="to-more-comment">
|
||||
<tm-button item-class="btn" :block="true" width="90vw" theme="bg-gradient-light-blue-lighten" size="m">点击查看全部评论</tm-button>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
export default {
|
||||
name: 'comment-list',
|
||||
components: { tmEmpty, tmButton },
|
||||
props: {
|
||||
// 是否禁用评论
|
||||
disallowComment: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
postId: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
post: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: 'loading',
|
||||
sort: 0,
|
||||
queryParams: {
|
||||
sort: '',
|
||||
more: true
|
||||
},
|
||||
api: 'getPostCommentTree',
|
||||
result: {},
|
||||
dataList: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.fnGetData();
|
||||
uni.$on('comment_list_refresh', () => {
|
||||
this.fnOnSort(this.sort, true);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
fnOnSort(type, refresh = false) {
|
||||
if (this.sort == type && refresh == false) return;
|
||||
const _api = ['getPostCommentTree', 'getPostTopCommentList'];
|
||||
this.sort = type;
|
||||
this.api = _api[type];
|
||||
this.fnGetData();
|
||||
},
|
||||
fnGetData() {
|
||||
this.loading = 'loading';
|
||||
this.$httpApi[this.api](this.postId, {})
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
this.result = res.data;
|
||||
this.dataList = res.data.content;
|
||||
this.loading = 'success';
|
||||
} else {
|
||||
this.loading = 'error';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.loading = 'error';
|
||||
})
|
||||
.finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
fnToDo() {
|
||||
uni.$tm.toast('Halo暂未支持!');
|
||||
},
|
||||
fnToComment(data) {
|
||||
if (this.disallowComment) {
|
||||
return uni.$tm.toast('文章已禁止评论!');
|
||||
}
|
||||
console.log(data);
|
||||
let _comment = {};
|
||||
if (data) {
|
||||
let { type, comment } = data;
|
||||
// 来自用户
|
||||
_comment = {
|
||||
id: this.post.id,
|
||||
parentId: comment.id,
|
||||
title: comment.author,
|
||||
from: 'posts',
|
||||
formPage: 'comment_list',
|
||||
type: 'user'
|
||||
};
|
||||
} else {
|
||||
// 来自文章
|
||||
_comment = {
|
||||
id: this.post.id,
|
||||
parentId: 0,
|
||||
title: '评论文章:' + this.post.title,
|
||||
formPage: 'comment_list',
|
||||
from: 'posts',
|
||||
type: 'post'
|
||||
};
|
||||
}
|
||||
|
||||
uni.$tm.vx.commit('comment/setCommentInfo', _comment);
|
||||
this.$Router.push({
|
||||
path: '/pagesA/comment/comment',
|
||||
query: _comment
|
||||
});
|
||||
},
|
||||
fnCopyContent(content) {
|
||||
uni.$tm.u.setClipboardData(content);
|
||||
uni.$tm.toast('内容已复制成功!');
|
||||
},
|
||||
|
||||
fnShowCommetnDetail(comment) {
|
||||
this.$emit('on-comment-detail', {
|
||||
postId: this.postId,
|
||||
comment: comment
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.comment-list {
|
||||
&_head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding-left: 24rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
top: 8rpx;
|
||||
width: 8rpx;
|
||||
height: 30rpx;
|
||||
background-color: rgb(3, 174, 252);
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.title {
|
||||
.count {
|
||||
font-size: 28rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
.filter {
|
||||
font-size: 26rpx;
|
||||
font-weight: normal;
|
||||
|
||||
&-item {
|
||||
margin-left: 20rpx;
|
||||
color: #666;
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
color: rgb(255, 152, 0);
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&_content {
|
||||
margin-top: 24rpx;
|
||||
padding-bottom: 36rpx;
|
||||
}
|
||||
}
|
||||
.loading-wrap {
|
||||
width: 100%;
|
||||
height: 506rpx;
|
||||
.loading {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.e-loading-icon {
|
||||
font-size: 100rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.to-more-comment {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 80rpx;
|
||||
::v-deep {
|
||||
.tm-button .tm-button-btn uni-button {
|
||||
height: 70rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,341 +0,0 @@
|
||||
// 轮播图
|
||||
|
||||
.Swiper-mfw-index-box {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
view {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.Swiper-mfw-index {
|
||||
// 轮播图
|
||||
width: 100%;
|
||||
.Swiper-mfw {
|
||||
width: inherit;
|
||||
height: 450rpx;
|
||||
border-radius: 12rpx;
|
||||
.swiper-mfw-item {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
border-radius: 12rpx;
|
||||
.Image,
|
||||
.ImageVideo {
|
||||
border-radius: 12rpx;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 指示器
|
||||
.Swiper-indicator-box {
|
||||
width: inherit;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
// Top顶部 [今日首推-盒子]
|
||||
.Top-date-hot {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
.left-date-ri {
|
||||
justify-content: center;
|
||||
.date-ri-text {
|
||||
color: #ffffff;
|
||||
font-size: 60rpx;
|
||||
font-weight: 700;
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
}
|
||||
.conter-date-nianyue {
|
||||
margin: 0 14rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
.left-width-bgcolor {
|
||||
width: 8rpx;
|
||||
height: 45rpx;
|
||||
border-radius: 12rpx;
|
||||
background-color: #fafafa;
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
.right-date-nianyue {
|
||||
flex-direction: column;
|
||||
.Top-yue-usa,
|
||||
.Bottom-nian,
|
||||
.text {
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
.text {
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right-hot-ttf {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-left: 6rpx;
|
||||
.hot-text {
|
||||
color: #ffffff;
|
||||
font-size: 52rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 指示器 [轮播信息 -> 标题,用户,头像,所在地]
|
||||
.Swiper-indicator-Top {
|
||||
position: absolute;
|
||||
left: 0rpx;
|
||||
bottom: 110rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 20rpx 24rpx;
|
||||
&.no-dot {
|
||||
bottom: 0;
|
||||
}
|
||||
.Top-item {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// 如果有视频,则显示“视频预览”
|
||||
.Top-ImageVideo {
|
||||
width: 150rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 2rpx 6rpx;
|
||||
margin-bottom: 10rpx;
|
||||
background-color: #f0ad4e;
|
||||
border-radius: 20rpx;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
.Icons {
|
||||
color: #242629;
|
||||
font-size: 26rpx;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
.ImageVideo-text {
|
||||
color: #242629;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 标题
|
||||
.Top-Title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
.title-text {
|
||||
width: 100%;
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
// 用户信息盒子
|
||||
.Bottom-UserInfo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
margin-top: 16rpx;
|
||||
align-items: center;
|
||||
.UserImage-box {
|
||||
width: 40rpx;
|
||||
border-radius: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
.Image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
.textbox {
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
.wo-text,
|
||||
.UserInfo {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.wo-text {
|
||||
color: #f1f2f6;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
.UserInfo {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.jiange-box {
|
||||
margin: 0 8rpx;
|
||||
.jiange-text {
|
||||
color: #f1f2f6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 指示器 [左边图片列表+右边按钮]
|
||||
.Swiper-indicator-Bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 12rpx;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
box-sizing: border-box;
|
||||
padding: 14rpx;
|
||||
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0.4), rgba(101, 101, 101, 0.7));
|
||||
// 左边[图片列表]
|
||||
.Bottom-left-Imagelist {
|
||||
// width: 560rpx;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
// 指示图(小图模式)
|
||||
.Bottom-item {
|
||||
width: 98rpx;
|
||||
height: 78rpx;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.Image {
|
||||
width: 98rpx;
|
||||
height: 78rpx;
|
||||
border-radius: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
&.current {
|
||||
.Image {
|
||||
// border: 4rpx solid #ffda02;
|
||||
border: 4rpx solid rgb(110, 186, 247);
|
||||
}
|
||||
}
|
||||
}
|
||||
.Bottom-item + .Bottom-item {
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
}
|
||||
// 右边 [历历在目-按钮]
|
||||
.Bottom-right-lili-btn {
|
||||
width: 145rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 10rpx;
|
||||
// background-image: linear-gradient(45deg, rgb(110, 186, 247), rgb(13, 141, 242));
|
||||
.Bottom-item {
|
||||
width: 145rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
.indicator-text {
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
.more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
font-size: 26rpx;
|
||||
.iconfont {
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
}
|
||||
.text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
// font-weight: normal;
|
||||
letter-spacing: 4rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.Swiper-box {
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden !important;
|
||||
transform: translateY(0) !important;
|
||||
transform: translateX(0) !important;
|
||||
&.right {
|
||||
.indicator-Top-box {
|
||||
position: absolute;
|
||||
}
|
||||
.Swiper-indicator-Top {
|
||||
bottom: 0;
|
||||
.Top-item .Top-Title .title-text {
|
||||
width: 540rpx;
|
||||
}
|
||||
}
|
||||
.Swiper-indicator-Bottom {
|
||||
width: 120rpx;
|
||||
position: absolute;
|
||||
top: 0rpx;
|
||||
left: initial;
|
||||
right: 0rpx;
|
||||
flex-direction: column;
|
||||
background-image: none;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
// border-radius: 0rpx 12rpx 12rpx 0;
|
||||
border-radius: 12rpx;
|
||||
.Bottom-left-Imagelist {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.Bottom-item {
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
.Bottom-item + .Bottom-item {
|
||||
margin-left: 0rpx;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
.Bottom-right-lili-btn {
|
||||
width: 100rpx;
|
||||
margin-left: -4rpx;
|
||||
.Bottom-item {
|
||||
width: initial;
|
||||
margin-top: 6rpx;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
.more {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.text {
|
||||
letter-spacing: 0;
|
||||
font-size: 24rpx;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.Swiper-mfw {
|
||||
position: relative;
|
||||
}
|
||||
.indicator-Top-box {
|
||||
position: absolute;
|
||||
}
|
||||
.indicator-Btoom-box {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
<template>
|
||||
<!-- 轮播图 -->
|
||||
<view class="Swiper-mfw-index-box">
|
||||
<view class="Swiper-mfw-index Swiper-box" :class="[dotPosition]">
|
||||
<swiper
|
||||
class="Swiper-mfw"
|
||||
:style="{ height: height }"
|
||||
:circular="true"
|
||||
:indicator-dots="false"
|
||||
:autoplay="autoplay"
|
||||
:interval="3000"
|
||||
:duration="1000"
|
||||
:current="currentIndex"
|
||||
:disable-touch="disable_touch"
|
||||
@change="change"
|
||||
>
|
||||
<!-- 只需要前5条数据 -->
|
||||
<swiper-item class="swiper-mfw-item" v-if="index <= (dotPosition == 'right' ? 3 : 4)" v-for="(item, index) in list" :key="index">
|
||||
<!-- /*
|
||||
1. 这里不需要用api控制暂停视频
|
||||
2. 因为video标签上加了v-if="current==index"
|
||||
3. 当current == index时才会创建视频组件
|
||||
4. 否current != index则就销毁视频
|
||||
*/ -->
|
||||
<!-- 如果有视频,则显示视频-->
|
||||
<template v-if="item.mp4 && current == index">
|
||||
<video
|
||||
class="ImageVideo"
|
||||
:id="'ImageVideo' + index"
|
||||
:ref="'ImageVideo' + index"
|
||||
:src="item.mp4"
|
||||
:loop="true"
|
||||
:muted="false"
|
||||
:autoplay="current == index ? true : false"
|
||||
:controls="false"
|
||||
:show-fullscreen-btn="false"
|
||||
:show-play-btn="false"
|
||||
:enable-progress-gesture="false"
|
||||
:play-strategy="0"
|
||||
:poster="item.image || item.src"
|
||||
></video>
|
||||
</template>
|
||||
<!-- 否则显示图片 -->
|
||||
<image v-else :src="item.image || item.src" class="Image" mode="aspectFill" @click.stop="$emit('on-click', item)"></image>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<!-- 指示器 [Top] -->
|
||||
<view v-if="useTop" class="Swiper-indicator-box indicator-Top-box">
|
||||
<!-- Top顶部 [今日首推-盒子] -->
|
||||
<view class="Top-date-hot">
|
||||
<view class="left-date-ri">
|
||||
<text class="date-ri-text text">{{ date.month }}</text>
|
||||
</view>
|
||||
<view class="conter-date-nianyue">
|
||||
<view class="left-width-bgcolor"></view>
|
||||
<view class="right-date-nianyue">
|
||||
<text class="Top-yue-usa text">{{ date.monthEn }}</text>
|
||||
<text class="Bottom-nian text">{{ date.year }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right-hot-ttf">
|
||||
<text class="text hot-text">{{ title }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 指示器 标题区域 -->
|
||||
<view v-if="useTitle" class="Swiper-indicator-Top" :class="{ 'no-dot': !useDot }">
|
||||
<block v-for="(item, index) in list" :key="index">
|
||||
<view v-if="currentIndex == index" class="Top-item" :class="current == index ? 'current' : 'no'">
|
||||
<!-- 如果存在视频,则显示“视频预览”提示 -->
|
||||
<view v-if="item.mp4" class="Top-ImageVideo">
|
||||
<!-- icon图标 -->
|
||||
<view class="Icons">
|
||||
<!-- 播放按钮图标 -->
|
||||
<text class="iconfont icon-caret-right"></text>
|
||||
</view>
|
||||
<text class="text ImageVideo-text app-ttf">视频预览</text>
|
||||
</view>
|
||||
<!-- 标题 -->
|
||||
<view class="Top-Title">
|
||||
<text class="text title-text">{{ item.title }}</text>
|
||||
</view>
|
||||
<!-- 用户信息 -->
|
||||
<view class="Bottom-UserInfo">
|
||||
<!-- 头像 -->
|
||||
<view class="UserImage-box"><image :src="item.avatar" class="Image" mode="aspectFill"></image></view>
|
||||
<!-- 用户名 -->
|
||||
<view class="textbox UserName-box">
|
||||
<text class="text UserInfo">{{ item.nickname }}</text>
|
||||
</view>
|
||||
<view v-if="item.createTime" class="jiange-box"><text class="text jiange-text"></text></view>
|
||||
<view v-if="item.createTime" class="textbox UserGPS-box">
|
||||
<text class="text UserInfo">发布于 {{ item.createTime }}</text>
|
||||
</view>
|
||||
<view v-if="item.address" class="jiange-box"><text class="text jiange-text">·</text></view>
|
||||
<view v-if="item.address" class="textbox UserGPS-box">
|
||||
<text class="text UserInfo">{{ item.address }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- 指示器 [左边图片列表+右边按钮] -->
|
||||
<view v-if="useDot" class="Swiper-indicator-Bottom">
|
||||
<!-- 左边 -->
|
||||
<view class="Bottom-left-Imagelist">
|
||||
<block v-for="(item, index) in list" :key="index">
|
||||
<view
|
||||
class="Bottom-item"
|
||||
v-if="Number(index) <= (dotPosition == 'right' ? 3 : 4)"
|
||||
:class="currentIndex == index ? 'current' : 'no'"
|
||||
@click="SwiperIndTap(index)"
|
||||
>
|
||||
<image :src="item.image || item.src" class="Image" mode="aspectFill"></image>
|
||||
</view>
|
||||
</block>
|
||||
</view>
|
||||
<!-- 右边 -->
|
||||
<view class="Bottom-right-lili-btn">
|
||||
<view class="Bottom-item is-more">
|
||||
<view class="more" @click.stop="$emit('on-more')">
|
||||
MORE
|
||||
<text class="iconfont icon-caret-right"></text>
|
||||
</view>
|
||||
<text class="left text indicator-text">更多推荐</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'e-swiper',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '450rpx'
|
||||
},
|
||||
dotPosition: {
|
||||
type: String,
|
||||
default: 'bottom'
|
||||
},
|
||||
useTop: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useDot: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useTitle: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
useUser: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 轮播图 数据列表
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 当前选中的项(指示器坐标位置)
|
||||
current: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
// 是否自动轮播
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 是否禁止用户 touch 操作
|
||||
currentIndex: 0,
|
||||
disable_touch: false, //touch 用户划动引起swiper变化。
|
||||
date: {
|
||||
year: '-',
|
||||
monthEn: '-',
|
||||
month: '-'
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.currentIndex = this.current;
|
||||
const date = new Date();
|
||||
//将月份名称存储在数组中
|
||||
const monthArray = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
|
||||
|
||||
this.date.year = date.getFullYear();
|
||||
let month = date.getMonth() + 1;
|
||||
this.date.month = month < 10 ? '0' + month : month;
|
||||
this.date.monthEn = monthArray[date.getMonth()].toUpperCase();
|
||||
},
|
||||
methods: {
|
||||
// current 改变时会触发 change 事件,event.detail = {current: current, source: source}
|
||||
change(e) {
|
||||
let { current, source } = e.detail;
|
||||
//只有页面自动切换,手动切换时才轮播,其他不允许
|
||||
if (source === 'autoplay' || source === 'touch') {
|
||||
let event = {
|
||||
current: current
|
||||
};
|
||||
this.currentIndex = current;
|
||||
this.$emit('change', event);
|
||||
}
|
||||
},
|
||||
// 手动点击了指示器[小图模式]
|
||||
SwiperIndTap(e) {
|
||||
let index = e;
|
||||
let event = {
|
||||
current: index
|
||||
};
|
||||
this.currentIndex = index;
|
||||
this.$emit('change', event);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './e-swiper.scss';
|
||||
</style>
|
||||
@@ -1,177 +0,0 @@
|
||||
<template>
|
||||
<view class="journal-card mb-24 round-3 bg-white ">
|
||||
<view class="head pa-24 pb-0 flex flex-between">
|
||||
<view class="left flex">
|
||||
<cache-image
|
||||
class="avatar rounded"
|
||||
radius="50%"
|
||||
width="70rpx"
|
||||
height="70rpx"
|
||||
:url="bloggerInfo.avatar"
|
||||
:fileMd5="bloggerInfo.avatar"
|
||||
mode="scaleToFill"
|
||||
></cache-image>
|
||||
<view class="info pl-16 flex flex-col">
|
||||
<view class="nickname text-weight-b text-grey-darken-4">{{ bloggerInfo.nickname }}</view>
|
||||
<view class="mt-3 time text-size-m ">{{ $tm.dayjs(journal.createTime).format('YYYY-MM-DD HH:mm:ss') }}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="right">
|
||||
<tm-button v-if="useLike" :shadow="0" theme="light-blue" size="s" @click="fnLike(journal)">点赞({{ journal.likes }})</tm-button>
|
||||
<tm-button v-if="useEdit" :shadow="0" theme="light-blue" size="s" @click="$emit('on-edit', journal)">编辑</tm-button>
|
||||
<tm-button v-if="useDel" :shadow="0" theme="red" size="s" @click="fnDel(journal)">删除</tm-button>
|
||||
</view>
|
||||
</view>
|
||||
<tm-more v-if="journal.content.length > 50" :maxHeight="100" label="查看全部内容" open-label="隐藏部分内容">
|
||||
<mp-html
|
||||
class="evan-markdown"
|
||||
lazy-load
|
||||
:domain="markdownConfig.domain"
|
||||
:loading-img="markdownConfig.loadingGif"
|
||||
:scroll-table="true"
|
||||
:selectable="true"
|
||||
:tag-style="markdownConfig.tagStyle"
|
||||
:container-style="markdownConfig.containStyle"
|
||||
:content="journal.content"
|
||||
:markdown="true"
|
||||
:showLineNumber="true"
|
||||
:showLanguageName="true"
|
||||
:copyByLongPress="true"
|
||||
/>
|
||||
</tm-more>
|
||||
<mp-html
|
||||
v-else
|
||||
class="evan-markdown"
|
||||
lazy-load
|
||||
:domain="markdownConfig.domain"
|
||||
:loading-img="markdownConfig.loadingGif"
|
||||
:scroll-table="true"
|
||||
:selectable="true"
|
||||
:tag-style="markdownConfig.tagStyle"
|
||||
:container-style="markdownConfig.containStyle"
|
||||
:content="journal.content"
|
||||
:markdown="true"
|
||||
:showLineNumber="true"
|
||||
:showLanguageName="true"
|
||||
:copyByLongPress="true"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MarkdownConfig from '@/common/markdown/markdown.config.js';
|
||||
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
|
||||
import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
|
||||
import tmMore from '@/tm-vuetify/components/tm-more/tm-more.vue';
|
||||
export default {
|
||||
name: 'journal-card',
|
||||
components: { mpHtml, tmButton, tmMore },
|
||||
props: {
|
||||
isAdmin: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
journal: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
useLike: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useDel: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
markdownConfig: MarkdownConfig
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 获取博主信息
|
||||
bloggerInfo() {
|
||||
let blogger = this.$tm.vx.getters().blogger.getBlogger;
|
||||
blogger.avatar = this.$utils.checkAvatarUrl(blogger.avatar, true);
|
||||
return blogger;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fnLike(journal) {
|
||||
uni.showLoading({
|
||||
mask: true,
|
||||
title: '正在点赞中...'
|
||||
});
|
||||
this.$httpApi
|
||||
.postJournalLikes(journal.id)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
journal.likes += 1;
|
||||
uni.$tm.toast('o( ̄▽ ̄)d点赞成功!');
|
||||
} else {
|
||||
uni.$tm.toast('Ծ‸Ծ点赞失败了~');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
uni.$tm.toast('Ծ‸Ծ点赞失败了~');
|
||||
});
|
||||
},
|
||||
|
||||
fnDel(journal) {
|
||||
uni.$eShowModal({
|
||||
title: '提示',
|
||||
content: '您确定要删除该日记吗?',
|
||||
showCancel: true,
|
||||
cancelText: '否',
|
||||
cancelColor: '#999999',
|
||||
confirmText: '是',
|
||||
confirmColor: '#03a9f4'
|
||||
})
|
||||
.then(res => {
|
||||
this.$httpApi.admin
|
||||
.deleteJournalsById(journal.id)
|
||||
.then(res => {
|
||||
if (res.status == 200) {
|
||||
this.$emit('on-del', journal);
|
||||
uni.$tm.toast('删除成功!');
|
||||
} else {
|
||||
uni.$tm.toast('Ծ‸Ծ删除失败~');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
uni.$tm.toast('Ծ‸Ծ删除失败~');
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.journal-card {
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
.avatar {
|
||||
width: 70rpx;
|
||||
height: 70rpx;
|
||||
border: 6rpx solid #fff;
|
||||
box-shadow: 0rpx 0rpx 24rpx rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.info {
|
||||
justify-content: center;
|
||||
.nickname {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
.time {
|
||||
font-size: 26rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,250 +0,0 @@
|
||||
<template>
|
||||
<view style="overflow: hidden;position: fixed;width: 100%;height: 100%;pointer-events: none; top: 0;">
|
||||
<view class="danmu-li" v-for="(item, index) in listData" :class="item.type" :style="item.style" :key="index">
|
||||
<view class="danmu-inner">
|
||||
<view class="user-box">
|
||||
<view class="user-img">
|
||||
<view class="img-box">
|
||||
<image :src="item.avatar || 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=317894666,3379114684&fm=26&gp=0.jpg'"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="user-text cl1">{{ item.nickName }}</view>
|
||||
<view class="user-status cl1">{{ item.text }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
//rightToLeft leftToRight leftBottom
|
||||
type: {
|
||||
type: String,
|
||||
default: 'rightToLeft'
|
||||
},
|
||||
minTime: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
maxTime: {
|
||||
type: Number,
|
||||
default: 9
|
||||
},
|
||||
minTop: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
maxTop: {
|
||||
type: Number,
|
||||
default: 240
|
||||
},
|
||||
hrackH: {
|
||||
//轨道高度
|
||||
type: Number,
|
||||
default: 40
|
||||
},
|
||||
noStacked: {
|
||||
//不允许堆叠(暂不可用)
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
listData: []
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.hrackNum = Math.floor((this.maxTop - this.minTop) / this.hrackH);
|
||||
},
|
||||
methods: {
|
||||
add(obj) {
|
||||
let data = {
|
||||
item: obj.item,
|
||||
id: Date.parse(new Date()),
|
||||
time: Math.ceil(Math.floor(Math.random() * (this.maxTime - this.minTime + 1) + this.minTime)),
|
||||
type: this.type
|
||||
};
|
||||
if (this.type === 'leftBottom') {
|
||||
let objData = {
|
||||
item: data.item,
|
||||
type: 'leftBottomEnter',
|
||||
style: {
|
||||
transition: `all 0.5s`,
|
||||
animationDuration: `0.5s`,
|
||||
transform: `translateX(0%)`,
|
||||
bottom: `${this.minTop}px`
|
||||
}
|
||||
};
|
||||
let listLen = this.listData.length;
|
||||
let hrackNum = this.hrackNum;
|
||||
for (let i in this.listData) {
|
||||
if (this.listData[i].status === 'reuse') {
|
||||
//重用
|
||||
this.$set(this.listData, i, objData);
|
||||
} else if (this.listData[i].status === 'reset') {
|
||||
//重置
|
||||
this.listData[i].style.transition = 'none';
|
||||
this.listData[i].style.bottom = 0;
|
||||
this.listData[i].status = 'reuse';
|
||||
} else if (this.listData[i].status === 'recycle') {
|
||||
//回收
|
||||
this.listData[i].type = 'leftBottomExit';
|
||||
this.listData[i].status = 'reset';
|
||||
} else {
|
||||
this.listData[i].style.bottom = parseInt(this.listData[i].style.bottom) + this.hrackH + 'px';
|
||||
}
|
||||
if (parseInt(this.listData[i].style.bottom) >= this.maxTop - this.hrackH && this.listData[i].status !== 'reset') {
|
||||
//需要回收
|
||||
this.listData[i].status = 'recycle';
|
||||
}
|
||||
}
|
||||
if (listLen < hrackNum + 2) {
|
||||
this.listData.push(objData);
|
||||
}
|
||||
} else if (this.type === 'rightToLeft' || this.type === 'leftToRight') {
|
||||
let objData = this.horStacked(data);
|
||||
for (let i in this.listData) {
|
||||
if (this.listData[i].delTime <= Date.parse(new Date())) {
|
||||
this.repaint(i, objData.type);
|
||||
objData.type = '';
|
||||
this.$set(this.listData, i, objData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.listData.push(objData);
|
||||
}
|
||||
},
|
||||
horStacked(data) {
|
||||
return {
|
||||
item: data.item,
|
||||
type: this.type,
|
||||
style: {
|
||||
animationDuration: `${data.time}s`,
|
||||
top: `${Math.ceil(Math.random() * (this.maxTop - this.minTop + 1) + this.minTop)}px`
|
||||
},
|
||||
delTime: Date.parse(new Date()) + data.time * 1200
|
||||
};
|
||||
},
|
||||
repaint(index, type) {
|
||||
setTimeout(() => {
|
||||
this.listData[index].type = type;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style></style>
|
||||
<style lang="scss">
|
||||
@keyframes leftBottomEnter {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leftBottomExit {
|
||||
0% {
|
||||
transform: translateY(0%);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-200%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes leftToRight {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rightToLeft {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.danmu-li {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
transform: translateX(100%);
|
||||
animation-timing-function: linear;
|
||||
|
||||
&.leftBottomEnter {
|
||||
animation-name: leftBottomEnter;
|
||||
}
|
||||
&.leftBottomExit {
|
||||
animation-name: leftBottomExit;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
&.rightToLeft {
|
||||
animation-name: rightToLeft;
|
||||
}
|
||||
|
||||
&.leftToRight {
|
||||
animation-name: leftToRight;
|
||||
}
|
||||
|
||||
.danmu-inner {
|
||||
display: inline-block;
|
||||
|
||||
.user-box {
|
||||
display: flex;
|
||||
padding: 3rpx 40rpx 3rpx 10rpx;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 32rpx;
|
||||
align-items: center;
|
||||
|
||||
.user-img {
|
||||
.img-box {
|
||||
display: flex;
|
||||
|
||||
image {
|
||||
width: 58rpx;
|
||||
height: 58rpx;
|
||||
background: rgba(55, 55, 55, 1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-status {
|
||||
margin-left: 10rpx;
|
||||
white-space: nowrap;
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.user-text {
|
||||
margin-left: 10rpx;
|
||||
// white-space: nowrap;
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
width: 80rpx;
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,15 +0,0 @@
|
||||
#如何使用
|
||||
###js
|
||||
```javascript
|
||||
import lffBarrage from '@/components/lff-barrage/lff-barrage.vue'
|
||||
components:{lffBarrage},
|
||||
methods:{
|
||||
colrdo(){ //插入一条弹幕
|
||||
this.$refs.lffBarrage.add({item:'你好呀小伙子'});
|
||||
}
|
||||
}
|
||||
```
|
||||
###HTML
|
||||
```html
|
||||
<lff-barrage ref="lffBarrage"></lff-barrage>
|
||||
```
|
||||
@@ -1,735 +0,0 @@
|
||||
export default{
|
||||
data(){
|
||||
return{
|
||||
system_info:{}, //system info
|
||||
canvas_width:0, //canvas width px
|
||||
canvas_height:0, //canvas height px
|
||||
ctx:null, //canvas object
|
||||
canvas_id:null, //canvas id
|
||||
hidden:false,//Whether to hide canvas
|
||||
scale:1,//canvas scale
|
||||
r_canvas_scale:1,
|
||||
if_ctx:true
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
/**
|
||||
* save r-canvas.vue object
|
||||
* @param {Object} that
|
||||
*/
|
||||
// saveThis(that){
|
||||
// rCanvasThis = that
|
||||
// },
|
||||
/**
|
||||
* Draw round rect text
|
||||
* @param {Object} config
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {Number} config.w 宽度
|
||||
* @param {Number} config.h 高度
|
||||
* @param {Number} config.radius 圆角弧度
|
||||
* @param {String} config.fill_color 矩形颜色
|
||||
*/
|
||||
fillRoundRect(config) {
|
||||
return new Promise((resolve,reject)=>{
|
||||
let x = this.compatibilitySize(parseFloat(config.x)*this.scale)
|
||||
let y = this.compatibilitySize(parseFloat(config.y)*this.scale)
|
||||
let w = this.compatibilitySize(parseFloat(config.w)*this.scale)
|
||||
let h = this.compatibilitySize(parseFloat(config.h)*this.scale)
|
||||
let radius = config.radius?parseFloat(config.radius)*this.scale:10*this.scale
|
||||
|
||||
let fill_color = config.fill_color || "black"
|
||||
// The diameter of the circle must be less than the width and height of the rectangle
|
||||
if (2 * radius > w || 2 * radius > h) {
|
||||
reject("The diameter of the circle must be less than the width and height of the rectangle")
|
||||
return false;
|
||||
}
|
||||
this.ctx.save();
|
||||
this.ctx.translate(x, y);
|
||||
//
|
||||
this.drawRoundRectPath({
|
||||
w: w,
|
||||
h: h,
|
||||
radius: radius
|
||||
});
|
||||
this.ctx.fillStyle = fill_color
|
||||
this.ctx.fill();
|
||||
this.ctx.restore();
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Draws the sides of a rounded rectangle
|
||||
* @param {Object} config
|
||||
* @param {Number} config.w 宽度
|
||||
* @param {Number} config.h 高度
|
||||
* @param {Number} config.radius 圆角弧度
|
||||
*/
|
||||
drawRoundRectPath(config) {
|
||||
this.ctx.beginPath(0);
|
||||
this.ctx.arc(config.w - config.radius, config.h - config.radius, config.radius, 0, Math.PI / 2);
|
||||
this.ctx.lineTo(config.radius, config.h);
|
||||
this.ctx.arc(config.radius, config.h - config.radius, config.radius, Math.PI / 2, Math.PI);
|
||||
this.ctx.lineTo(0, config.radius);
|
||||
this.ctx.arc(config.radius, config.radius, config.radius, Math.PI, Math.PI * 3 / 2);
|
||||
this.ctx.lineTo(config.w - config.radius, 0);
|
||||
this.ctx.arc(config.w - config.radius, config.radius, config.radius, Math.PI * 3 / 2, Math.PI * 2);
|
||||
this.ctx.lineTo(config.w, config.h - config.radius);
|
||||
this.ctx.closePath();
|
||||
},
|
||||
/**
|
||||
* Draw special Text,line wrapping is not supported
|
||||
* @param {Object} config
|
||||
* @param {String} config.text 文字
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {String} config.font_color 文字颜色
|
||||
* @param {String} config.font_family 文字字体
|
||||
* @param {Number} config.font_size 文字大小(px)
|
||||
*/
|
||||
drawSpecialText(params){
|
||||
let general = params.general
|
||||
let list = params.list
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!general){
|
||||
reject("general cannot be empty:101")
|
||||
return;
|
||||
}else if(list && list.length>0){
|
||||
for(let i in list){
|
||||
if(i != 0){
|
||||
let font_size = list[i-1].font_size?parseFloat(list[i-1].font_size):20
|
||||
this.ctx.setFontSize(font_size)
|
||||
general.x = parseFloat(general.x) + this.ctx.measureText(list[i-1].text).width
|
||||
}
|
||||
list[i].x = general.x
|
||||
list[i].y = general.y + (list[i].margin_top?parseFloat(list[i].margin_top):0)
|
||||
await this.drawText(list[i])
|
||||
}
|
||||
resolve()
|
||||
}else{
|
||||
reject("The length of config arr is less than 0")
|
||||
return;
|
||||
}
|
||||
|
||||
})
|
||||
},
|
||||
/**
|
||||
* array delete empty
|
||||
* @param {Object} arr
|
||||
*/
|
||||
arrDeleteEmpty(arr){
|
||||
let newArr = []
|
||||
for(let i in arr){
|
||||
if(arr[i]){
|
||||
newArr.push(arr[i])
|
||||
}
|
||||
}
|
||||
return newArr
|
||||
},
|
||||
/**
|
||||
* Draw Text,support line
|
||||
* @param {Object} config
|
||||
* @param {String} config.text 文字
|
||||
* @param {Number} config.max_width 文字最大宽度(大于宽度自动换行)
|
||||
* @param {Number} config.line_height 文字上下行间距
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {String} config.font_color 文字颜色
|
||||
* @param {String} config.font_family 文字字体 默认值:Arial
|
||||
* @param {String} config.text_align 文字对齐方式(left/center/right)
|
||||
* @param {Number} config.font_size 文字大小(px)
|
||||
* @param {Boolean} config.line_through_height 中划线大小
|
||||
* @param {Boolean} config.line_through_color 中划线颜色
|
||||
* @param {String} config.font_style 规定文字样式
|
||||
* @param {String} config.font_variant 规定字体变体
|
||||
* @param {String} config.font_weight 规定字体粗细
|
||||
* @param {String} config.line_through_cap 线末端类型
|
||||
* @param {String} config.line_clamp 最大行数
|
||||
* @param {String} config.line_clamp_hint 超过line_clamp后,尾部显示的自定义标识 如 ...
|
||||
* @param {String} config.is_line_break 是否开启换行符换行
|
||||
*
|
||||
*/
|
||||
drawText(config,configuration = {}){
|
||||
|
||||
configuration['line_num'] = configuration.line_num?configuration.line_num:0
|
||||
configuration['text_width'] = configuration.text_width?configuration.text_width:0
|
||||
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
|
||||
if(config.text){
|
||||
|
||||
let draw_width = 0,draw_height = 0,draw_x = config.x,draw_y = config.y
|
||||
let font_size = config.font_size?(parseFloat(config.font_size)*this.scale):(20*this.scale)
|
||||
let font_color = config.font_color || "#000"
|
||||
let font_family = config.font_family || "Arial"
|
||||
let line_height = config.line_height || config.font_size || 20
|
||||
let text_align = config.text_align || "left"
|
||||
let font_weight = config.font_weight || "normal"
|
||||
let font_variant = config.font_variant || "normal"
|
||||
let font_style = config.font_style || "normal"
|
||||
let line_clamp_hint = config.line_clamp_hint || '...'
|
||||
let lineBreakJoinText = ""
|
||||
let max_width = config.max_width?parseFloat(config.max_width)*this.scale:0
|
||||
// checkout is line break
|
||||
if(config.is_line_break){
|
||||
let splitTextArr = config.text.split(/[\n]/g)
|
||||
if(splitTextArr && splitTextArr.length > 0){
|
||||
let newSplitTextArr = this.arrDeleteEmpty(splitTextArr)
|
||||
if(newSplitTextArr && newSplitTextArr.length > 0){
|
||||
lineBreakJoinText = newSplitTextArr.slice(1).join("\n")
|
||||
config.text = newSplitTextArr[0]
|
||||
}else{
|
||||
reject("Text cannot be empty:103")
|
||||
return
|
||||
}
|
||||
}else{
|
||||
reject("Text cannot be empty:102")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.ctx.setFillStyle(font_color) // color
|
||||
this.ctx.textAlign = text_align;
|
||||
this.ctx.font = `${font_style} ${font_variant} ${font_weight} ${parseInt(font_size)}px ${font_family}`
|
||||
if(configuration.text_width >= this.ctx.measureText(config.text).width){
|
||||
draw_width = configuration.text_width
|
||||
}else if(max_width > 0){
|
||||
draw_width = max_width < this.ctx.measureText(config.text).width ? this.resetCompatibilitySize(max_width) : this.resetCompatibilitySize(this.ctx.measureText(config.text).width)
|
||||
}else{
|
||||
draw_width = this.ctx.measureText(config.text).width
|
||||
}
|
||||
configuration.text_width = draw_width / this.scale
|
||||
if( max_width && this.compatibilitySize(this.ctx.measureText(config.text).width) > this.compatibilitySize(max_width)){
|
||||
let current_text = ""
|
||||
let text_arr = config.text.split("")
|
||||
for(let i in text_arr){
|
||||
if( this.compatibilitySize(this.ctx.measureText(current_text+text_arr[i]).width) > this.compatibilitySize(max_width) ){
|
||||
// Hyphenation that is greater than the drawable width continues to draw
|
||||
if(config.line_clamp && parseInt(config.line_clamp) == 1){
|
||||
// Subtracting the current_text tail width from the line_clamp_hint width
|
||||
let current_text_arr = current_text.split('')
|
||||
let json_current_text = ''
|
||||
while(true){
|
||||
current_text_arr = current_text_arr.slice(1)
|
||||
json_current_text = current_text_arr.join('')
|
||||
if(this.compatibilitySize(this.ctx.measureText(json_current_text).width) <= this.compatibilitySize(this.ctx.measureText(line_clamp_hint).width)){
|
||||
current_text = current_text.replace(json_current_text,'')
|
||||
break;
|
||||
}
|
||||
}
|
||||
configuration.line_num += 1
|
||||
this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
|
||||
this.ctx.fillText(current_text + line_clamp_hint, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
|
||||
}else{
|
||||
configuration.line_num += 1
|
||||
this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
|
||||
this.ctx.fillText(current_text, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
|
||||
config.text = text_arr.slice(i).join("")
|
||||
config.y = config.y + line_height
|
||||
if(config.line_clamp){
|
||||
config.line_clamp = parseInt(config.line_clamp) - 1
|
||||
}
|
||||
await this.drawText(config,configuration)
|
||||
}
|
||||
|
||||
break;
|
||||
}else{
|
||||
current_text = current_text+text_arr[i]
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(config.line_through_height){
|
||||
let x = parseFloat(config.x)*this.scale
|
||||
let w
|
||||
let y = parseFloat(config.y)*this.scale - (font_size / 2.6)
|
||||
if(text_align == "left"){
|
||||
w = this.ctx.measureText(config.text).width/1.1 + parseFloat(config.x)*this.scale
|
||||
}else if(text_align == "right"){
|
||||
w = parseFloat(config.x)*this.scale - this.ctx.measureText(config.text).width/1.1
|
||||
}else if(text_align == "center"){
|
||||
x = parseFloat(config.x)*this.scale - this.ctx.measureText(config.text).width / 1.1 / 2
|
||||
w = parseFloat(config.x)*this.scale + this.ctx.measureText(config.text).width / 1.1 / 2
|
||||
}
|
||||
this.drawLineTo({
|
||||
x:x,
|
||||
y:y,
|
||||
w:w,
|
||||
h:y,
|
||||
line_width:config.line_through_height,
|
||||
line_color:config.line_through_color,
|
||||
line_cap:config.line_through_cap
|
||||
})
|
||||
}
|
||||
configuration.line_num += 1
|
||||
this.ctx.setFontSize(parseInt(this.compatibilitySize(font_size))) // font size
|
||||
this.ctx.fillText(config.text, this.compatibilitySize(parseFloat(config.x)*this.scale), this.compatibilitySize(parseFloat(config.y)*this.scale));
|
||||
if(config.line_clamp){
|
||||
config.line_clamp = parseInt(config.line_clamp) - 1
|
||||
}
|
||||
}
|
||||
if(lineBreakJoinText){
|
||||
await this.drawText({...config,text:lineBreakJoinText,y:config.y + line_height},configuration)
|
||||
}
|
||||
draw_height = config.font_size * configuration.line_num
|
||||
draw_width = configuration.text_width
|
||||
resolve({draw_width,draw_height,draw_x,draw_y})
|
||||
}else{
|
||||
reject("Text cannot be empty:101")
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Draw Line
|
||||
* @param {Object} config
|
||||
* @param {Object} config.x x坐标
|
||||
* @param {Object} config.y y坐标
|
||||
* @param {Object} config.w 线的宽度
|
||||
* @param {Object} config.h 线的高度
|
||||
* @param {Object} config.line_width 线的宽度
|
||||
* @param {Object} config.line_color 线条颜色
|
||||
*/
|
||||
drawLineTo(config){
|
||||
let x = this.compatibilitySize(config.x)
|
||||
let y = this.compatibilitySize(config.y)
|
||||
let w = this.compatibilitySize(config.w)
|
||||
let h = this.compatibilitySize(config.h)
|
||||
let line_width = config.line_width?parseFloat(config.line_width)*this.scale:1*this.scale
|
||||
let line_color = config.line_color || "black"
|
||||
let line_cap = config.line_cap || "butt"
|
||||
this.ctx.beginPath()
|
||||
this.ctx.lineCap = line_cap
|
||||
this.ctx.lineWidth = line_width
|
||||
this.ctx.strokeStyle = line_color
|
||||
this.ctx.moveTo(x,y)
|
||||
this.ctx.lineTo(w,h)
|
||||
this.ctx.stroke()
|
||||
},
|
||||
/**
|
||||
* Compatibility px
|
||||
* @param {Object} size
|
||||
*/
|
||||
compatibilitySize(size) {
|
||||
let canvasSize = (parseFloat(size) / 750) * this.system_info.windowWidth
|
||||
canvasSize = parseFloat(canvasSize * 2)
|
||||
return canvasSize
|
||||
},
|
||||
/**
|
||||
* Restore compatibility px
|
||||
* @param {Object} size
|
||||
*/
|
||||
resetCompatibilitySize(size) {
|
||||
let canvasSize = (parseFloat(size/2)/this.system_info.windowWidth) * 750
|
||||
return canvasSize
|
||||
},
|
||||
/**
|
||||
* Init canvas
|
||||
*/
|
||||
init(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!config.canvas_id){
|
||||
reject("Canvas ID cannot be empty, please refer to the usage example")
|
||||
return;
|
||||
}
|
||||
this.hidden = config.hidden
|
||||
this.canvas_id = config.canvas_id
|
||||
let system_info = await uni.getSystemInfoSync()
|
||||
this.system_info = system_info
|
||||
this.scale = config.scale&&parseFloat(config.scale)>0?parseInt(config.scale):1
|
||||
this.canvas_width = (config.canvas_width ? this.compatibilitySize(config.canvas_width) : system_info.windowWidth) * this.scale
|
||||
this.canvas_height = (config.canvas_height ? this.compatibilitySize(config.canvas_height) : system_info.windowHeight) * this.scale,
|
||||
this.r_canvas_scale = 1/this.scale
|
||||
this.ctx = uni.createCanvasContext(this.canvas_id,this)
|
||||
this.setCanvasConfig({
|
||||
global_alpha:config.global_alpha?parseFloat(config.global_alpha):1,
|
||||
backgroundColor:config.background_color?config.background_color:"#fff"
|
||||
})
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* clear canvas all path
|
||||
*/
|
||||
clearCanvas(){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!this.ctx){
|
||||
reject("canvas is not initialized:101")
|
||||
return
|
||||
}else{
|
||||
this.ctx.clearRect(0,0,parseFloat(this.canvas_width)*this.scale,parseFloat(this.canvas_height)*this.scale)
|
||||
await this.draw()
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Set canvas config
|
||||
* @param {Object} config
|
||||
*/
|
||||
setCanvasConfig(config){
|
||||
this.ctx.globalAlpha = config.global_alpha
|
||||
this.ctx.fillStyle = config.backgroundColor
|
||||
this.ctx.fillRect(0, 0, parseFloat(this.canvas_width)*this.scale, parseFloat(this.canvas_height)*this.scale)
|
||||
},
|
||||
/**
|
||||
* set canvas width
|
||||
* @param {Object} width
|
||||
*/
|
||||
setCanvasWidth(width){
|
||||
if(!width){
|
||||
uni.showToast({
|
||||
title:'setCanvasWidth:width error',
|
||||
icon:'none'
|
||||
})
|
||||
}
|
||||
this.canvas_width = this.compatibilitySize(parseFloat(width)) * this.scale
|
||||
this.ctx.width = this.canvas_width
|
||||
},
|
||||
/**
|
||||
* set canvas height
|
||||
* @param {Object} height
|
||||
*/
|
||||
setCanvasHeight(height){
|
||||
if(!height){
|
||||
uni.showToast({
|
||||
title:'setCanvasWidth:height error',
|
||||
icon:'none'
|
||||
})
|
||||
}
|
||||
this.canvas_height = this.compatibilitySize(parseFloat(height)) * this.scale
|
||||
this.ctx.height = this.canvas_height
|
||||
},
|
||||
/**
|
||||
* Draw to filepath
|
||||
*/
|
||||
draw(callback){
|
||||
return new Promise((resolve,reject)=>{
|
||||
let stop = setTimeout(()=>{
|
||||
this.ctx.draw(false,setTimeout(()=>{
|
||||
uni.canvasToTempFilePath({
|
||||
canvasId: this.canvas_id,
|
||||
quality: 1,
|
||||
success: (res)=>{
|
||||
console.log('res',res)
|
||||
resolve(res)
|
||||
callback && callback(res)
|
||||
},
|
||||
fail:(err)=>{
|
||||
reject(JSON.stringify(err)|| "Failed to generate poster:101")
|
||||
}
|
||||
},this)
|
||||
},300))
|
||||
clearTimeout(stop)
|
||||
},300)
|
||||
})
|
||||
},
|
||||
/**
|
||||
* draw rect
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {Number} config.w 图形宽度(px)
|
||||
* @param {Number} config.h 图形高度(px)
|
||||
* @param {Number} config.color 图形颜色
|
||||
* @param {Number} config.is_radius 是否开启圆图(1.1.6及以下版本废弃,请使用border_radius)
|
||||
* @param {Number} config.border_width 边框大小
|
||||
* @param {Number} config.border_color 边框颜色
|
||||
*
|
||||
*/
|
||||
drawRect(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(!config.border_width || config.border_width <=0){
|
||||
config.border_width = 0
|
||||
}else{
|
||||
config.border_width = parseFloat(config.border_width)
|
||||
}
|
||||
if(parseFloat(config.border_width) > 0){
|
||||
let sub_config = JSON.parse(JSON.stringify(config))
|
||||
sub_config.border_width = 0
|
||||
sub_config.w = config.w + config.border_width
|
||||
sub_config.h = config.h + config.border_width
|
||||
sub_config.color = config.border_color || 'black'
|
||||
if(sub_config.border_radius){
|
||||
sub_config.border_radius = parseFloat(sub_config.border_radius) + parseFloat(config.border_width) / 2
|
||||
}
|
||||
await this.drawRect(sub_config)
|
||||
}
|
||||
|
||||
let color = config.color || 'white'
|
||||
config.x = (parseFloat(config.x) + config.border_width / 2)
|
||||
config.y = (parseFloat(config.y) + config.border_width / 2)
|
||||
config['color'] = color
|
||||
this.ctx.fillStyle = color;
|
||||
if(config.is_radius || config.border_radius){
|
||||
this.setNativeBorderRadius(config)
|
||||
this.ctx.fill()
|
||||
}else{
|
||||
console.log('config.border_width',config.border_width)
|
||||
this.ctx.fillRect(this.compatibilitySize(config.x*this.scale),this.compatibilitySize(config.y*this.scale),this.compatibilitySize(parseFloat(config.w)*this.scale),this.compatibilitySize(parseFloat(config.h)*this.scale))
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Draw image
|
||||
* @param {Object} config
|
||||
* @param {String} config.url 图片链接
|
||||
* @param {Number} config.x x坐标
|
||||
* @param {Number} config.y y坐标
|
||||
* @param {Number} config.w 图片宽度(px)
|
||||
* @param {Number} config.h 图片高度(px)
|
||||
* @param {Number} config.border_width 边大小
|
||||
* @param {Number} config.border_color 边颜色
|
||||
* @param {Number} config.is_radius 是否开启圆图(1.1.6及以下版本废弃,请使用border_radius)
|
||||
* @param {Number} config.border_radius 圆角弧度
|
||||
*/
|
||||
drawImage(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if(config.url){
|
||||
let type = 0 // 1、network image 2、native image 3、base64 image
|
||||
let image_url
|
||||
let reg = /^https?/ig;
|
||||
if(reg.test(config.url)){
|
||||
type = 1
|
||||
}else{
|
||||
if((config.url.indexOf("data:image/png;base64") != -1) || config.url.indexOf("data:image/jpeg;base64") != -1 || config.url.indexOf("data:image/gif;base64") != -1){
|
||||
type = 3
|
||||
}else{
|
||||
type = 2
|
||||
}
|
||||
}
|
||||
if(type == 1){
|
||||
// network image
|
||||
await this.downLoadNetworkFile(config.url).then(res=>{ // two function
|
||||
image_url = res
|
||||
}).catch(err=>{
|
||||
reject(err)
|
||||
return;
|
||||
})
|
||||
}else if(type == 2){
|
||||
// native image
|
||||
const imageInfoResult = await uni.getImageInfo({
|
||||
src: config.url
|
||||
});
|
||||
try{
|
||||
if(imageInfoResult.length <= 1){
|
||||
reject(imageInfoResult[0].errMsg + ':404')
|
||||
return
|
||||
}
|
||||
}catch(e){
|
||||
reject(e+':500')
|
||||
return
|
||||
}
|
||||
let base64 = await this.urlToBase64({url:imageInfoResult[1].path})
|
||||
// #ifdef MP-WEIXIN
|
||||
await this.base64ToNative({url:base64}).then(res=>{
|
||||
image_url = res
|
||||
}).catch(err=>{
|
||||
reject(JSON.stringify(err)+":501")
|
||||
return;
|
||||
})
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
image_url = base64
|
||||
// #endif
|
||||
|
||||
}else if(type == 3){
|
||||
// #ifdef MP-WEIXIN
|
||||
await this.base64ToNative({url:config.url}).then(res=>{
|
||||
image_url = res
|
||||
}).catch(err=>{
|
||||
reject(JSON.stringify(err)+":500")
|
||||
return;
|
||||
})
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
image_url = config.url
|
||||
// #endif
|
||||
}else{
|
||||
reject("Other Type Errors:101")
|
||||
return
|
||||
}
|
||||
if(config.border_width){
|
||||
let border_radius = 0
|
||||
if(config.border_radius){
|
||||
let multiple = config.w / config.border_radius
|
||||
border_radius = (parseFloat(config.w) + parseFloat(config.border_width)) / multiple
|
||||
}
|
||||
// drawRect
|
||||
await this.drawRect({
|
||||
x:parseFloat(config.x) - parseFloat(config.border_width)/2,
|
||||
y:parseFloat(config.y) - parseFloat(config.border_width)/2,
|
||||
w:parseFloat(config.w) + parseFloat(config.border_width),
|
||||
h:parseFloat(config.h) + parseFloat(config.border_width),
|
||||
color:config.border_color,
|
||||
border_radius:border_radius,
|
||||
border_width:config.border_width,
|
||||
is_radius:config.is_radius
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(config.border_radius){
|
||||
config.color = config.color?config.color:'rgba(0,0,0,0)'
|
||||
|
||||
// 圆角有白边,+0.5的误差
|
||||
config.w = config.w + 0.3
|
||||
config.h = config.h + 0.3
|
||||
|
||||
this.setNativeBorderRadius(config)
|
||||
}else if(config.is_radius){
|
||||
//已废弃 is_radius
|
||||
this.ctx.setStrokeStyle("rgba(0,0,0,0)")
|
||||
this.ctx.save()
|
||||
this.ctx.beginPath()
|
||||
this.ctx.arc(this.compatibilitySize(parseFloat(config.x)*this.scale+parseFloat(config.w)*this.scale/2), this.compatibilitySize(parseFloat(config.y)*this.scale+parseFloat(config.h)*this.scale/2), this.compatibilitySize(parseFloat(config.w)*this.scale/2), 0, 2 * Math.PI, false)
|
||||
this.ctx.stroke();
|
||||
this.ctx.clip()
|
||||
}
|
||||
|
||||
await this.ctx.drawImage(image_url,this.compatibilitySize(parseFloat(config.x)*this.scale),this.compatibilitySize(parseFloat(config.y)*this.scale),this.compatibilitySize(parseFloat(config.w)*this.scale),this.compatibilitySize(parseFloat(config.h)*this.scale))
|
||||
this.ctx.restore() //Restore previously saved drawing context
|
||||
resolve()
|
||||
}else{
|
||||
let err_msg = "Links cannot be empty:101"
|
||||
reject(err_msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* base64 to native available path
|
||||
* @param {Object} config
|
||||
*/
|
||||
base64ToNative(config){
|
||||
return new Promise((resolve,reject)=>{
|
||||
let fileName = new Date().getTime()
|
||||
var filePath = `${wx.env.USER_DATA_PATH}/${fileName}_rCanvas.png`
|
||||
wx.getFileSystemManager().writeFile({
|
||||
filePath: filePath,
|
||||
data: config.url.replace(/^data:\S+\/\S+;base64,/, ''),
|
||||
encoding: 'base64',
|
||||
success: function() {
|
||||
resolve(filePath)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* native url to base64
|
||||
* @param {Object} config
|
||||
*/
|
||||
urlToBase64(config){
|
||||
return new Promise(async (resolve,reject)=>{
|
||||
if (typeof window != 'undefined') {
|
||||
await this.downLoadNetworkFile(config.url).then(res=>{ // two function
|
||||
resolve(res)
|
||||
}).catch(err=>{
|
||||
reject(err)
|
||||
})
|
||||
}else if (typeof plus != 'undefined') {
|
||||
plus.io.resolveLocalFileSystemURL(config.url,(obj)=>{
|
||||
obj.file((file)=>{
|
||||
let fileReader = new plus.io.FileReader()
|
||||
fileReader.onload = (res)=>{
|
||||
resolve(res.target.result)
|
||||
}
|
||||
fileReader.onerror = (err)=>{
|
||||
reject(err)
|
||||
}
|
||||
fileReader.readAsDataURL(file)
|
||||
}, (err)=>{
|
||||
reject(err)
|
||||
})
|
||||
},(err)=>{
|
||||
reject(err)
|
||||
})
|
||||
}else if(typeof wx != 'undefined'){
|
||||
wx.getFileSystemManager().readFile({
|
||||
filePath: config.url,
|
||||
encoding: 'base64',
|
||||
success: function(res) {
|
||||
resolve('data:image/png;base64,' + res.data)
|
||||
},
|
||||
fail: function(error) {
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
setNativeBorderRadius(config){
|
||||
let border_radius = config.border_radius?(parseFloat(config.border_radius)*this.scale):(20*this.scale)
|
||||
if ((parseFloat(config.w)*this.scale) < 2 * border_radius) border_radius = (parseFloat(config.w)*this.scale) / 2;
|
||||
if ((parseFloat(config.h)*this.scale) < 2 * border_radius) border_radius = (parseFloat(config.h)*this.scale) / 2;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + border_radius), this.compatibilitySize((parseFloat(config.y)*this.scale)));
|
||||
this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.arcTo((this.compatibilitySize(parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale) + (parseFloat(config.h)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.arcTo(this.compatibilitySize((parseFloat(config.x)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize((parseFloat(config.x)*this.scale) + (parseFloat(config.w)*this.scale)), this.compatibilitySize((parseFloat(config.y)*this.scale)), this.compatibilitySize(border_radius));
|
||||
this.ctx.closePath();
|
||||
this.ctx.strokeStyle = config.color || config.border_color || 'rgba(0,0,0,0)'; // 设置绘制边框的颜色
|
||||
this.ctx.stroke();
|
||||
this.ctx.save()
|
||||
this.ctx.clip();
|
||||
|
||||
},
|
||||
/**
|
||||
* Download network file
|
||||
* @param {Object} url : download url
|
||||
*/
|
||||
downLoadNetworkFile(url){
|
||||
return new Promise((resolve,reject)=>{
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success:(res)=>{
|
||||
if(res.statusCode == 200){
|
||||
resolve(res.tempFilePath)
|
||||
}else{
|
||||
reject("Download Image Fail:102")
|
||||
}
|
||||
},
|
||||
fail:(err)=>{
|
||||
reject("Download Image Fail:101")
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
/**
|
||||
* Save image to natice
|
||||
* @param {Object} filePath : native imageUrl
|
||||
*/
|
||||
saveImage(filePath){
|
||||
return new Promise((resolve,reject)=>{
|
||||
if(!filePath){
|
||||
reject("FilePath cannot be null:101")
|
||||
return;
|
||||
}
|
||||
|
||||
// #ifdef H5
|
||||
var createA = document.createElement("a");
|
||||
createA.download = filePath;
|
||||
createA.href = filePath;
|
||||
document.body.appendChild(createA);
|
||||
createA.click();
|
||||
createA.remove();
|
||||
resolve()
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: filePath,
|
||||
success:(res)=>{
|
||||
resolve(res)
|
||||
},
|
||||
fail:(err)=>{
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="r-canvas-component" :style="{width:canvas_width/scale+'px',height:canvas_height/scale+'px'}" :class="{'hidden':hidden}">
|
||||
<canvas class="r-canvas" v-if="canvas_id" :canvas-id="canvas_id" :id="canvas_id" :style="{width:canvas_width+'px',height:canvas_height+'px','transform': `scale(${r_canvas_scale})`}"></canvas>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import rCanvasJS from "./r-canvas.js"
|
||||
export default {
|
||||
mixins:[rCanvasJS]
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.r-canvas{
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
.r-canvas-component{
|
||||
overflow: hidden;
|
||||
}
|
||||
.r-canvas-component.hidden{
|
||||
position: fixed;
|
||||
top:-5000upx;
|
||||
}
|
||||
</style>
|
||||
@@ -1,784 +0,0 @@
|
||||
<template>
|
||||
<view v-show="show" class="t-wrapper" @touchmove.stop.prevent="moveHandle">
|
||||
<view class="t-mask" :class="{active:active}" @click.stop="close"></view>
|
||||
<view class="t-box" :class="{active:active}">
|
||||
<view class="t-header">
|
||||
<view class="t-header-button" @click="close">取消</view>
|
||||
<view class="t-header-button" @click="confirm">确认</view>
|
||||
</view>
|
||||
<view class="t-color__box" :style="{ background: 'rgb(' + bgcolor.r + ',' + bgcolor.g + ',' + bgcolor.b + ')'}">
|
||||
<view class="t-background boxs" @touchstart="touchstart($event, 0)" @touchmove="touchmove($event, 0)" @touchend="touchend($event, 0)">
|
||||
<view class="t-color-mask"></view>
|
||||
<view class="t-pointer" :style="{ top: site[0].top - 8 + 'px', left: site[0].left - 8 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-control__box">
|
||||
<view class="t-control__color">
|
||||
<view class="t-control__color-content" :style="{ background: 'rgba(' + rgba.r + ',' + rgba.g + ',' + rgba.b + ',' + rgba.a + ')' }"></view>
|
||||
</view>
|
||||
<view class="t-control-box__item">
|
||||
<view class="t-controller boxs" @touchstart="touchstart($event, 1)" @touchmove="touchmove($event, 1)" @touchend="touchend($event, 1)">
|
||||
<view class="t-hue">
|
||||
<view class="t-circle" :style="{ left: site[1].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-controller boxs" @touchstart="touchstart($event, 2)" @touchmove="touchmove($event, 2)" @touchend="touchend($event, 2)">
|
||||
<view class="t-transparency">
|
||||
<view class="t-circle" :style="{ left: site[2].left - 12 + 'px' }"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-result__box">
|
||||
<view v-if="mode" class="t-result__item">
|
||||
<view class="t-result__box-input">{{hex}}</view>
|
||||
<view class="t-result__box-text">HEX</view>
|
||||
</view>
|
||||
<template v-else>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.r}}</view>
|
||||
<view class="t-result__box-text">R</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.g}}</view>
|
||||
<view class="t-result__box-text">G</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.b}}</view>
|
||||
<view class="t-result__box-text">B</view>
|
||||
</view>
|
||||
<view class="t-result__item">
|
||||
<view class="t-result__box-input">{{rgba.a}}</view>
|
||||
<view class="t-result__box-text">A</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<view class="t-result__item t-select" @click="select">
|
||||
<view class="t-result__box-input">
|
||||
<view>切换</view>
|
||||
<view>模式</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="t-alternative">
|
||||
<view class="t-alternative__item" v-for="(item,index) in colorList" :key="index">
|
||||
<view class="t-alternative__item-content" :style="{ background: 'rgba(' + item.r + ',' + item.g + ',' + item.b + ',' + item.a + ')' }"
|
||||
@click="selectColor(item)">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
spareColor: {
|
||||
type: Array,
|
||||
default () {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
active: false,
|
||||
// rgba 颜色
|
||||
rgba: {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
// hsb 颜色
|
||||
hsb: {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
},
|
||||
site: [{
|
||||
top: 0,
|
||||
left: 0
|
||||
}, {
|
||||
left: 0
|
||||
}, {
|
||||
left: 0
|
||||
}],
|
||||
index: 0,
|
||||
bgcolor: {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 1
|
||||
},
|
||||
hex: '#000000',
|
||||
mode: true,
|
||||
colorList: [{
|
||||
r: 244,
|
||||
g: 67,
|
||||
b: 54,
|
||||
a: 1
|
||||
}, {
|
||||
r: 233,
|
||||
g: 30,
|
||||
b: 99,
|
||||
a: 1
|
||||
}, {
|
||||
r: 156,
|
||||
g: 39,
|
||||
b: 176,
|
||||
a: 1
|
||||
}, {
|
||||
r: 103,
|
||||
g: 58,
|
||||
b: 183,
|
||||
a: 1
|
||||
}, {
|
||||
r: 63,
|
||||
g: 81,
|
||||
b: 181,
|
||||
a: 1
|
||||
}, {
|
||||
r: 33,
|
||||
g: 150,
|
||||
b: 243,
|
||||
a: 1
|
||||
}, {
|
||||
r: 3,
|
||||
g: 169,
|
||||
b: 244,
|
||||
a: 1
|
||||
}, {
|
||||
r: 0,
|
||||
g: 188,
|
||||
b: 212,
|
||||
a: 1
|
||||
}, {
|
||||
r: 0,
|
||||
g: 150,
|
||||
b: 136,
|
||||
a: 1
|
||||
}, {
|
||||
r: 76,
|
||||
g: 175,
|
||||
b: 80,
|
||||
a: 1
|
||||
}, {
|
||||
r: 139,
|
||||
g: 195,
|
||||
b: 74,
|
||||
a: 1
|
||||
}, {
|
||||
r: 205,
|
||||
g: 220,
|
||||
b: 57,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 235,
|
||||
b: 59,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 193,
|
||||
b: 7,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 152,
|
||||
b: 0,
|
||||
a: 1
|
||||
}, {
|
||||
r: 255,
|
||||
g: 87,
|
||||
b: 34,
|
||||
a: 1
|
||||
}, {
|
||||
r: 121,
|
||||
g: 85,
|
||||
b: 72,
|
||||
a: 1
|
||||
}, {
|
||||
r: 158,
|
||||
g: 158,
|
||||
b: 158,
|
||||
a: 1
|
||||
}, {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0.5
|
||||
}, {
|
||||
r: 0,
|
||||
g: 0,
|
||||
b: 0,
|
||||
a: 0
|
||||
}, ]
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.rgba = this.color;
|
||||
if (this.spareColor.length !== 0) {
|
||||
this.colorList = this.spareColor;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
init() {
|
||||
// hsb 颜色
|
||||
this.hsb = this.rgbToHex(this.rgba);
|
||||
// this.setColor();
|
||||
this.setValue(this.rgba);
|
||||
},
|
||||
moveHandle() {},
|
||||
open() {
|
||||
this.show = true;
|
||||
this.$nextTick(() => {
|
||||
this.init();
|
||||
setTimeout(() => {
|
||||
this.active = true;
|
||||
setTimeout(() => {
|
||||
this.getSelectorQuery();
|
||||
}, 350)
|
||||
}, 50)
|
||||
})
|
||||
|
||||
},
|
||||
close() {
|
||||
this.active = false;
|
||||
this.$nextTick(() => {
|
||||
setTimeout(() => {
|
||||
this.show = false;
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
confirm() {
|
||||
this.close();
|
||||
this.$emit('confirm', {
|
||||
rgba: this.rgba,
|
||||
hex: this.hex
|
||||
})
|
||||
},
|
||||
// 选择模式
|
||||
select() {
|
||||
this.mode = !this.mode
|
||||
},
|
||||
// 常用颜色选择
|
||||
selectColor(item) {
|
||||
this.setColorBySelect(item)
|
||||
},
|
||||
touchstart(e, index) {
|
||||
const {
|
||||
pageX,
|
||||
pageY
|
||||
} = e.touches[0];
|
||||
this.pageX = pageX;
|
||||
this.pageY = pageY;
|
||||
this.setPosition(pageX, pageY, index);
|
||||
},
|
||||
touchmove(e, index) {
|
||||
const {
|
||||
pageX,
|
||||
pageY
|
||||
} = e.touches[0];
|
||||
this.moveX = pageX;
|
||||
this.moveY = pageY;
|
||||
this.setPosition(pageX, pageY, index);
|
||||
},
|
||||
touchend(e, index) {},
|
||||
/**
|
||||
* 设置位置
|
||||
*/
|
||||
setPosition(x, y, index) {
|
||||
this.index = index;
|
||||
const {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height
|
||||
} = this.position[index];
|
||||
// 设置最大最小值
|
||||
|
||||
this.site[index].left = Math.max(0, Math.min(parseInt(x - left), width));
|
||||
if (index === 0) {
|
||||
this.site[index].top = Math.max(0, Math.min(parseInt(y - top), height));
|
||||
// 设置颜色
|
||||
this.hsb.s = parseInt((100 * this.site[index].left) / width);
|
||||
this.hsb.b = parseInt(100 - (100 * this.site[index].top) / height);
|
||||
this.setColor();
|
||||
this.setValue(this.rgba);
|
||||
} else {
|
||||
this.setControl(index, this.site[index].left);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 设置 rgb 颜色
|
||||
*/
|
||||
setColor() {
|
||||
const rgb = this.HSBToRGB(this.hsb);
|
||||
this.rgba.r = rgb.r;
|
||||
this.rgba.g = rgb.g;
|
||||
this.rgba.b = rgb.b;
|
||||
},
|
||||
/**
|
||||
* 设置二进制颜色
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
setValue(rgb) {
|
||||
this.hex = '#' + this.rgbToHex(rgb);
|
||||
},
|
||||
setControl(index, x) {
|
||||
const {
|
||||
top,
|
||||
left,
|
||||
width,
|
||||
height
|
||||
} = this.position[index];
|
||||
|
||||
if (index === 1) {
|
||||
this.hsb.h = parseInt((360 * x) / width);
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
});
|
||||
this.setColor()
|
||||
} else {
|
||||
this.rgba.a = (x / width).toFixed(1);
|
||||
}
|
||||
this.setValue(this.rgba);
|
||||
},
|
||||
/**
|
||||
* rgb 转 二进制 hex
|
||||
* @param {Object} rgb
|
||||
*/
|
||||
rgbToHex(rgb) {
|
||||
let hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];
|
||||
hex.map(function(str, i) {
|
||||
if (str.length == 1) {
|
||||
hex[i] = '0' + str;
|
||||
}
|
||||
});
|
||||
return hex.join('');
|
||||
},
|
||||
setColorBySelect(getrgb) {
|
||||
const {
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
a
|
||||
} = getrgb;
|
||||
let rgb = {}
|
||||
rgb = {
|
||||
r: r ? parseInt(r) : 0,
|
||||
g: g ? parseInt(g) : 0,
|
||||
b: b ? parseInt(b) : 0,
|
||||
a: a ? a : 0,
|
||||
};
|
||||
this.rgba = rgb;
|
||||
this.hsb = this.rgbToHsb(rgb);
|
||||
this.changeViewByHsb();
|
||||
},
|
||||
changeViewByHsb() {
|
||||
const [a, b, c] = this.position;
|
||||
this.site[0].left = parseInt(this.hsb.s * a.width / 100);
|
||||
this.site[0].top = parseInt((100 - this.hsb.b) * a.height / 100);
|
||||
this.setColor(this.hsb.h);
|
||||
this.setValue(this.rgba);
|
||||
this.bgcolor = this.HSBToRGB({
|
||||
h: this.hsb.h,
|
||||
s: 100,
|
||||
b: 100
|
||||
});
|
||||
|
||||
this.site[1].left = this.hsb.h / 360 * b.width;
|
||||
this.site[2].left = this.rgba.a * c.width;
|
||||
|
||||
},
|
||||
/**
|
||||
* hsb 转 rgb
|
||||
* @param {Object} 颜色模式 H(hues)表示色相,S(saturation)表示饱和度,B(brightness)表示亮度
|
||||
*/
|
||||
HSBToRGB(hsb) {
|
||||
let rgb = {};
|
||||
let h = Math.round(hsb.h);
|
||||
let s = Math.round((hsb.s * 255) / 100);
|
||||
let v = Math.round((hsb.b * 255) / 100);
|
||||
if (s == 0) {
|
||||
rgb.r = rgb.g = rgb.b = v;
|
||||
} else {
|
||||
let t1 = v;
|
||||
let t2 = ((255 - s) * v) / 255;
|
||||
let t3 = ((t1 - t2) * (h % 60)) / 60;
|
||||
if (h == 360) h = 0;
|
||||
if (h < 60) {
|
||||
rgb.r = t1;
|
||||
rgb.b = t2;
|
||||
rgb.g = t2 + t3;
|
||||
} else if (h < 120) {
|
||||
rgb.g = t1;
|
||||
rgb.b = t2;
|
||||
rgb.r = t1 - t3;
|
||||
} else if (h < 180) {
|
||||
rgb.g = t1;
|
||||
rgb.r = t2;
|
||||
rgb.b = t2 + t3;
|
||||
} else if (h < 240) {
|
||||
rgb.b = t1;
|
||||
rgb.r = t2;
|
||||
rgb.g = t1 - t3;
|
||||
} else if (h < 300) {
|
||||
rgb.b = t1;
|
||||
rgb.g = t2;
|
||||
rgb.r = t2 + t3;
|
||||
} else if (h < 360) {
|
||||
rgb.r = t1;
|
||||
rgb.g = t2;
|
||||
rgb.b = t1 - t3;
|
||||
} else {
|
||||
rgb.r = 0;
|
||||
rgb.g = 0;
|
||||
rgb.b = 0;
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: Math.round(rgb.r),
|
||||
g: Math.round(rgb.g),
|
||||
b: Math.round(rgb.b)
|
||||
};
|
||||
},
|
||||
rgbToHsb(rgb) {
|
||||
let hsb = {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
};
|
||||
let min = Math.min(rgb.r, rgb.g, rgb.b);
|
||||
let max = Math.max(rgb.r, rgb.g, rgb.b);
|
||||
let delta = max - min;
|
||||
hsb.b = max;
|
||||
hsb.s = max != 0 ? 255 * delta / max : 0;
|
||||
if (hsb.s != 0) {
|
||||
if (rgb.r == max) hsb.h = (rgb.g - rgb.b) / delta;
|
||||
else if (rgb.g == max) hsb.h = 2 + (rgb.b - rgb.r) / delta;
|
||||
else hsb.h = 4 + (rgb.r - rgb.g) / delta;
|
||||
} else hsb.h = -1;
|
||||
hsb.h *= 60;
|
||||
if (hsb.h < 0) hsb.h = 0;
|
||||
hsb.s *= 100 / 255;
|
||||
hsb.b *= 100 / 255;
|
||||
return hsb;
|
||||
},
|
||||
getSelectorQuery() {
|
||||
const views = uni.createSelectorQuery().in(this);
|
||||
views
|
||||
.selectAll('.boxs')
|
||||
.boundingClientRect(data => {
|
||||
if (!data || data.length === 0) {
|
||||
setTimeout(() => this.getSelectorQuery(), 20)
|
||||
return
|
||||
}
|
||||
this.position = data;
|
||||
// this.site[0].top = data[0].height;
|
||||
// this.site[0].left = 0;
|
||||
// this.site[1].left = data[1].width;
|
||||
// this.site[2].left = data[2].width;
|
||||
this.setColorBySelect(this.rgba);
|
||||
})
|
||||
.exec();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
spareColor(newVal) {
|
||||
this.colorList = newVal;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.t-wrapper {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.t-box {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 30upx 0;
|
||||
padding-top: 0;
|
||||
background: #fff;
|
||||
transition: all 0.3s;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.t-box.active {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
.t-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 100upx;
|
||||
border-bottom: 1px #eee solid;
|
||||
box-shadow: 1px 0 2px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-header-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 150upx;
|
||||
height: 100upx;
|
||||
font-size: 30upx;
|
||||
color: #666;
|
||||
padding-left: 20upx;
|
||||
}
|
||||
|
||||
.t-header-button:last-child {
|
||||
justify-content: flex-end;
|
||||
padding-right: 20upx;
|
||||
}
|
||||
|
||||
.t-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: -1;
|
||||
transition: all 0.3s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.t-mask.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.t-color__box {
|
||||
position: relative;
|
||||
height: 400upx;
|
||||
background: rgb(255, 0, 0);
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
margin: 0 20upx;
|
||||
margin-top: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: linear-gradient(to right, #fff, rgba(255, 255, 255, 0));
|
||||
}
|
||||
|
||||
.t-color-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 400upx;
|
||||
background: linear-gradient(to top, #000, rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
.t-pointer {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: -8px;
|
||||
z-index: 2;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border: 1px #fff solid;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.t-show-color {
|
||||
width: 100upx;
|
||||
height: 50upx;
|
||||
}
|
||||
|
||||
.t-control__box {
|
||||
margin-top: 50upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
padding-left: 20upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-control__color {
|
||||
flex-shrink: 0;
|
||||
width: 100upx;
|
||||
height: 100upx;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.t-control__color-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.t-control-box__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 0 30upx;
|
||||
}
|
||||
|
||||
.t-controller {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 16px;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 32upx 32upx;
|
||||
background-position: 0 0, 16upx 16upx;
|
||||
}
|
||||
|
||||
.t-hue {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%);
|
||||
}
|
||||
|
||||
.t-transparency {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, rgba(0, 0, 0, 0) 0%, rgb(0, 0, 0));
|
||||
}
|
||||
|
||||
.t-circle {
|
||||
position: absolute;
|
||||
/* right: -10px; */
|
||||
top: -2px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.t-result__box {
|
||||
margin-top: 20upx;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 10upx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-result__box-input {
|
||||
padding: 10upx 0;
|
||||
width: 100%;
|
||||
font-size: 28upx;
|
||||
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
color: #999;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-result__box-text {
|
||||
margin-top: 10upx;
|
||||
font-size: 28upx;
|
||||
line-height: 2;
|
||||
}
|
||||
|
||||
.t-select {
|
||||
flex-shrink: 0;
|
||||
width: 150upx;
|
||||
padding: 0 30upx;
|
||||
}
|
||||
|
||||
.t-select .t-result__box-input {
|
||||
border-radius: 10upx;
|
||||
border: none;
|
||||
color: #999;
|
||||
box-shadow: 1px 1px 2px 1px rgba(0, 0, 0, 0.1);
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.t-select .t-result__box-input:active {
|
||||
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.t-alternative {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/* justify-content: space-between; */
|
||||
width: 100%;
|
||||
padding-right: 10upx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.t-alternative__item {
|
||||
margin-left: 12upx;
|
||||
margin-top: 10upx;
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
border-radius: 10upx;
|
||||
background-color: #fff;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 36upx 36upx;
|
||||
background-position: 0 0, 18upx 18upx;
|
||||
border: 1px #eee solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.t-alternative__item-content {
|
||||
width: 50upx;
|
||||
height: 50upx;
|
||||
background: rgba(255, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.t-alternative__item:active {
|
||||
transition: all 0.3s;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
</style>
|
||||
@@ -1,89 +0,0 @@
|
||||
<template>
|
||||
<view class="wave-wrap waveAnimation">
|
||||
<view class="waveWrapperInner bgTop"><view class="wave waveTop"></view></view>
|
||||
<view class="waveWrapperInner bgMiddle"><view class="wave waveMiddle"></view></view>
|
||||
<view class="waveWrapperInner bgBottom"><view class="wave waveBottom"></view></view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'wave',
|
||||
props: {
|
||||
height: {
|
||||
type: String,
|
||||
default: '35rpx'
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.wave-wrap {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
margin: auto;
|
||||
}
|
||||
.waveWrapperInner {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
.wave {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 200%;
|
||||
height: 100%;
|
||||
background-repeat: repeat no-repeat;
|
||||
background-position: 0 bottom;
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
.bgTop {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.waveTop {
|
||||
background-size: 50% 45px;
|
||||
background-image: url('~@/static/wave/wave-1.png');
|
||||
}
|
||||
.waveAnimation .waveTop {
|
||||
animation: move_wave 4s linear infinite;
|
||||
}
|
||||
@keyframes move_wave {
|
||||
0% {
|
||||
transform: translateX(0) translateZ(0) scaleY(1);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(-25%) translateZ(0) scaleY(1);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-50%) translateZ(0) scaleY(1);
|
||||
}
|
||||
}
|
||||
.bgMiddle {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.waveMiddle {
|
||||
background-size: 50% 40px;
|
||||
background-image: url('~@/static/wave/wave-2.png');
|
||||
}
|
||||
.waveAnimation .waveMiddle {
|
||||
animation: move_wave 3.5s linear infinite;
|
||||
}
|
||||
|
||||
.bgBottom {
|
||||
opacity: 0.95;
|
||||
}
|
||||
.waveBottom {
|
||||
background-size: 50% 35px;
|
||||
background-image: url('~@/static/wave/wave-1.png');
|
||||
}
|
||||
.waveAnimation .waveBottom {
|
||||
animation: move_wave 2s linear infinite;
|
||||
}
|
||||
</style>
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* 广告配置
|
||||
*/
|
||||
export default {
|
||||
adpid: '', // uni-AD App广告位id,在uni-AD官网申请广告位
|
||||
unitId: '', // 广告单元id,可在小程序管理后台的流量主模块新建 (非个人资质,小程序后台广告主开通申请)
|
||||
frequency: 8, // 列表中,广告出现的频率(8=每8条数据出现一次广告)
|
||||
// 首页广告
|
||||
home: {
|
||||
use: false,
|
||||
},
|
||||
// 文章列表广告
|
||||
articles: {
|
||||
use: false,
|
||||
},
|
||||
// 文章详情广告
|
||||
articleDetail: {
|
||||
use: false, // 是否启用
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
/**
|
||||
* 功能:基础配置
|
||||
* 作者:小莫唐尼
|
||||
* 邮箱:studio@925i.cn
|
||||
* 时间:2022年08月23日 15:19:14
|
||||
* 版本:v0.1.0
|
||||
* 修改记录:
|
||||
* 修改内容:
|
||||
* 修改人员:
|
||||
* 修改时间:
|
||||
*/
|
||||
export default {
|
||||
showCopyright: true, // 显示开源版权信息
|
||||
showAbout: true, // 显示关于项目入口
|
||||
uni_halo_logo: 'https://b.925i.cn/uni_halo/uni_halo_logo.png', // uni-halo的logo
|
||||
|
||||
apiUrl: '', // Api基础域名(您的halo博客基础域名或者是Halo后台管理系统api地址)
|
||||
apiAuthorization: '', // Halo中-系统-博客设置-切换到高级选项-API设置-Access key
|
||||
|
||||
title: '', // 博客标题
|
||||
indexImageUrl: '', // 开屏首页图片
|
||||
miniCodeImageUrl: '', // 小程序码地址
|
||||
|
||||
author: {
|
||||
name: '', // 昵称
|
||||
avatar: '', // 头像地址
|
||||
motto: '', // 格言
|
||||
},
|
||||
|
||||
social: {
|
||||
qq: "", // qq号
|
||||
wechat: "", // 微信号
|
||||
weibo: "", // 微博地址
|
||||
email: "", // 邮箱地址
|
||||
blog: "", // 博客地址
|
||||
juejin: "", // 掘金地址
|
||||
bilibili: "", // b站地址
|
||||
gitee: "", // gitee地址
|
||||
github: "", // github地址
|
||||
csdn: "" // CSDN地址
|
||||
},
|
||||
|
||||
defaultThumbnailUrl: '', // 默认封面图地址
|
||||
defaultImageUrl: '', // 默认图片地址
|
||||
defaultAvatarUrl: '', // 默认头像地址
|
||||
|
||||
loadingGifUrl: '', // 图片加载中的地址
|
||||
loadingErrUrl: '', // 图片加载失败的地址
|
||||
loadingEmptyUrl: '', // 加载图片为空地址
|
||||
|
||||
waveImageUrl: '', // 关于页面波浪图片
|
||||
|
||||
banner: { // 轮播图配置
|
||||
type: 'article', // 轮播图数据源 list=下方配置 article=热门文章封面
|
||||
list: [],
|
||||
},
|
||||
|
||||
quickNav: { // 快捷导航配置
|
||||
use: true,
|
||||
list: [{
|
||||
icon: 'halocoloricon-classify',
|
||||
text: '文章归档',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/archives/archives'
|
||||
},
|
||||
{
|
||||
icon: 'halocoloricon-attent',
|
||||
text: '恋爱日记',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/love/love'
|
||||
},
|
||||
{
|
||||
icon: 'halocoloricon-calendar',
|
||||
text: '个人日记',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/journal/journal'
|
||||
},
|
||||
{
|
||||
icon: 'halocoloricon-message',
|
||||
text: '留言板',
|
||||
iconSize: 60,
|
||||
color: 'blue',
|
||||
type: 'page',
|
||||
path: '/pagesA/leaving/leaving'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// 微信分享信息
|
||||
wxShareConfig: {
|
||||
title: '', // 小程序分享标题[非必填]
|
||||
desc: '', // 小程序分享描述[非必填]
|
||||
imageUrl: '', // 小程序分享时候图片地址[非必填]
|
||||
path: '/pages/start/start', // 分享路径[非必填] - 基本不需要修改
|
||||
copyLink: '/pages/start/start', // 复制链接[非必填] - 基本不需要修改
|
||||
query: {}, // 分享参数[非必填] - 基本不需要填写
|
||||
},
|
||||
|
||||
colors: [
|
||||
'#39B449',
|
||||
'#E44C41',
|
||||
'#8698A2',
|
||||
'#0080FE',
|
||||
'#1CBCB4',
|
||||
'#6638B5',
|
||||
]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* 配置key
|
||||
*/
|
||||
|
||||
export default {
|
||||
SHEET_LEAVING: 'leaving', // 留言板
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* 页面配置
|
||||
*/
|
||||
import AppKeys from './keys.js'
|
||||
|
||||
export default {
|
||||
[AppKeys.SHEET_LEAVING]: 65, // 留言板页面ID
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
VITE_APP_TITLE='uni-halo'
|
||||
VITE_APP_DESC='基于halo开源博客系统API开发的多端博客。'
|
||||
VITE_APP_PORT=9000
|
||||
|
||||
VITE_UNI_APPID='__UNI__5748B6E'
|
||||
VITE_WX_APPID='wx1bb161867082c72f'
|
||||
|
||||
# 微信开发者工具 CLI 路径,仅当默认安装路径不正确时配置(就是当 pnpm dev:mp 无法自动打开微信开发者工具时,才需要配置,通常是你更改了默认的安装位置导致的,一般出现在windows系统)
|
||||
# macOS 示例:
|
||||
# WECHAT_DEVTOOLS_CLI_PATH = '/Applications/wechatwebdevtools.app/Contents/MacOS/cli'
|
||||
# Windows 示例:
|
||||
# WECHAT_DEVTOOLS_CLI_PATH = 'C:\Program Files (x86)\Tencent\微信web开发者工具\cli.bat'
|
||||
|
||||
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
|
||||
# https://uniapp.dcloud.net.cn/collocation/manifest.html#h5-router
|
||||
# 比如你要部署到 https://unibest.tech/doc/ ,则配置为 /doc/
|
||||
VITE_APP_PUBLIC_BASE=/
|
||||
|
||||
# 默认后台请求地址
|
||||
# 不同命令会按 mode 叠加读取 .env.development / .env.test / .env.production。
|
||||
# 微信小程序如果没有配置下面的专用地址,也会回退使用这个值。
|
||||
VITE_UNI_HALO_BASEURL=''
|
||||
# 备注:如果后台带统一前缀,则也要加到后面,eg: https://blog.xiaoxiaomo.cn/api
|
||||
|
||||
# 微信小程序专用后台请求地址,按微信开发者工具 envVersion 区分。
|
||||
# 不配置时会回退使用 VITE_UNI_HALO_BASEURL。
|
||||
# VITE_UNI_HALO_BASEURL__WEIXIN_DEVELOP = 'https://dev.xxx.com'
|
||||
# VITE_UNI_HALO_BASEURL__WEIXIN_TRIAL = 'https://trial.xxx.com'
|
||||
# VITE_UNI_HALO_BASEURL__WEIXIN_RELEASE = 'https://prod.xxx.com'
|
||||
|
||||
# h5是否需要配置代理
|
||||
VITE_APP_PROXY_ENABLE=false
|
||||
# 下面的不用修改,只要不跟你后台的统一前缀冲突就行。如果修改了,记得修改 `nginx` 里面的配置
|
||||
VITE_APP_PROXY_PREFIX='/dev-api'
|
||||
|
||||
# 第二个请求地址 (目前alova中可以使用)
|
||||
VITE_UNI_HALO_BASEURL_SECONDARY='https://blog.xiaoxiaomo.cn'
|
||||
|
||||
# 认证模式,'single' | 'double' ==> 单token | 双token
|
||||
VITE_AUTH_MODE='single'
|
||||
|
||||
# 原生插件资源复制开关,启用后 App 构建会把根目录 nativeplugins 复制到 dist
|
||||
VITE_COPY_NATIVE_RES_ENABLE=true
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV='development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE=false
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP=false
|
||||
|
||||
# development mode 后台请求地址
|
||||
# VITE_UNI_HALO_BASEURL='https://www.yijunzhao.cn'
|
||||
# VITE_UNI_HALO_BASEURL='https://www.xhhao.com'
|
||||
VITE_UNI_HALO_BASEURL='https://ryanc.cc'
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV='production'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE=true
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP=false
|
||||
|
||||
# production mode 后台请求地址
|
||||
# VITE_UNI_HALO_BASEURL = 'https://prod.xxx.com'
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV='development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE=false
|
||||
# 是否开启sourcemap
|
||||
VITE_SHOW_SOURCEMAP=false
|
||||
|
||||
# test mode 后台请求地址
|
||||
# VITE_UNI_HALO_BASEURL = 'https://test.xxx.com'
|
||||
@@ -0,0 +1,60 @@
|
||||
import uniHelper from '@uni-helper/eslint-config'
|
||||
|
||||
export default uniHelper({
|
||||
unocss: true,
|
||||
vue: true,
|
||||
markdown: false,
|
||||
ignores: [
|
||||
// 忽略uni_modules目录
|
||||
'**/uni_modules/',
|
||||
// 忽略原生插件目录
|
||||
'**/nativeplugins/',
|
||||
'dist',
|
||||
// unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
|
||||
'auto-import.d.ts',
|
||||
// vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
|
||||
'uni-pages.d.ts',
|
||||
// 插件生成的文件
|
||||
'src/pages.json',
|
||||
'src/manifest.json',
|
||||
// 忽略自动生成文件
|
||||
'src/service/**',
|
||||
],
|
||||
// https://eslint-config.antfu.me/rules
|
||||
rules: {
|
||||
'no-useless-return': 'off',
|
||||
'no-console': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
'vue/no-unused-refs': 'off',
|
||||
'unused-imports/no-unused-vars': 'off',
|
||||
'eslint-comments/no-unlimited-disable': 'off',
|
||||
'jsdoc/check-param-names': 'off',
|
||||
'jsdoc/require-returns-description': 'off',
|
||||
'ts/no-empty-object-type': 'off',
|
||||
'no-extend-native': 'off',
|
||||
// uni 条件编译注释可能包裹 import,自动排序会破坏平台条件边界
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'vue/singleline-html-element-content-newline': [
|
||||
'error',
|
||||
{
|
||||
externalIgnores: ['text'],
|
||||
},
|
||||
],
|
||||
// vue SFC 调换顺序改这里
|
||||
'vue/block-order': ['error', {
|
||||
order: [['script', 'template'], 'style'],
|
||||
}],
|
||||
},
|
||||
formatters: {
|
||||
/**
|
||||
* Format CSS, LESS, SCSS files, also the `<style>` blocks in Vue
|
||||
* By default uses Prettier
|
||||
*/
|
||||
css: true,
|
||||
/**
|
||||
* Format HTML files
|
||||
* By default uses Prettier
|
||||
*/
|
||||
html: true,
|
||||
},
|
||||
})
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
+13
-7
@@ -1,20 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!doctype html>
|
||||
<html build-time="%BUILD_TIME%">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
var coverSupport =
|
||||
'CSS' in window &&
|
||||
typeof CSS.supports === 'function' &&
|
||||
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
(coverSupport ? ', viewport-fit=cover' : '') +
|
||||
'" />',
|
||||
)
|
||||
</script>
|
||||
<title></title>
|
||||
<title>%VITE_APP_TITLE%</title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import FyShowModal from './showModal.js'
|
||||
|
||||
export default class Fy{
|
||||
/**
|
||||
* @author 大雄
|
||||
* @Date 2021年7月1日20:49:58
|
||||
* @description 二次封装showModel
|
||||
*/
|
||||
static showModal({ title = "提示", content = "提示内容", showCancel = true, backbutton = false, cancelText = "取消", cancelColor = "#000000", confirmText = "确定", confirmColor = '#3CC51F', complete = false } = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// #ifdef APP-PLUS
|
||||
if (this.get_app_platform() === 'android') { // android的使用自定义的模态框
|
||||
new FyShowModal({ title, content, showCancel, backbutton, cancelText, cancelColor, confirmText, confirmColor,
|
||||
success(res) {
|
||||
if (res.confirm) {resolve(res) } else { reject(res) }
|
||||
},
|
||||
fail(err){
|
||||
this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||
}
|
||||
}).show();
|
||||
} else { // ios直接用原生的
|
||||
return this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||
}
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
return this.uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }).then((res)=>resolve(res)).catch(err=>reject(err))
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
|
||||
// 原生showModal
|
||||
static uniShowModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor, complete }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let appPlatform = null;
|
||||
// #ifdef APP-PLUS
|
||||
if (this.get_app_platform() === 'android' && showCancel) { // android的确认按钮在左边,需要统一到右边
|
||||
appPlatform = 'android';
|
||||
var tempConfirmText = confirmText;
|
||||
var tempConfirmColor = confirmColor;
|
||||
confirmText = cancelText;
|
||||
cancelText = tempConfirmText;
|
||||
confirmColor = cancelColor;
|
||||
cancelColor = tempConfirmColor;
|
||||
}
|
||||
// #endif
|
||||
uni.showModal({ title, content, showCancel, cancelText, cancelColor, confirmText, confirmColor,
|
||||
success(res){
|
||||
if (complete) {
|
||||
resolve(res);
|
||||
} else if (res.confirm) {
|
||||
appPlatform === 'android' ? reject(res) : resolve(res)
|
||||
} else {
|
||||
appPlatform === 'android' ? resolve(res) : reject(res)
|
||||
}
|
||||
},
|
||||
fail(err){ reject(err) }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取app平台(android | ios)
|
||||
* */
|
||||
static get_app_platform() {
|
||||
// #ifndef APP-PLUS
|
||||
this.showModal({ content: '仅支持app' });
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
return plus.os.name.toLowerCase();
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/**
|
||||
* @author 大雄
|
||||
* @Date 2021年7月1日20:49:58
|
||||
* @description 路由导航守卫(简单版,后续需要功能再完善)
|
||||
*/
|
||||
export default function() {
|
||||
// 监听路由前进
|
||||
function routerPush({ type = 'navigateTo' } = {}) {
|
||||
routeWatchClearModal();
|
||||
}
|
||||
// 监听路由后退
|
||||
function routerBack() {
|
||||
routeWatchClearModal();
|
||||
}
|
||||
|
||||
// 页面跳转后,销毁当前页面未关闭的弹框
|
||||
function routeWatchClearModal() {
|
||||
try {
|
||||
var FyShowModalView = plus.nativeObj.View.getViewById("FyShowModalView");
|
||||
if (FyShowModalView) {
|
||||
FyShowModalView.clear();
|
||||
}
|
||||
var FyShowModalCancel = plus.nativeObj.View.getViewById("FyShowModalCancel");
|
||||
if (FyShowModalCancel) {
|
||||
FyShowModalCancel.clear();
|
||||
}
|
||||
var FyShowModalConfirm = plus.nativeObj.View.getViewById("FyShowModalConfirm");
|
||||
if (FyShowModalConfirm) {
|
||||
FyShowModalConfirm.clear();
|
||||
}
|
||||
} catch(err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
uni.addInterceptor('navigateTo', {
|
||||
success(e) {
|
||||
routerPush({ type: 'navigateTo' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('redirectTo', {
|
||||
success(e) {
|
||||
routerPush({ type: 'redirectTo' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('reLaunch', {
|
||||
success(e) {
|
||||
routerPush({ type: 'reLaunch' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('switchTab', {
|
||||
success(e) {
|
||||
routerPush({ type: 'switchTab' });
|
||||
}
|
||||
})
|
||||
uni.addInterceptor('navigateBack', {
|
||||
success(e) {
|
||||
routerBack();
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
/**
|
||||
* @description 替换android app的uni.showModal
|
||||
*/
|
||||
|
||||
let modalIntance = null;
|
||||
export class fyShowModal {
|
||||
constructor(options = {}) {
|
||||
modalIntance = this;
|
||||
|
||||
this.modalControl = null; // 模态框句柄
|
||||
this.cancelModel = null;
|
||||
this.confirmModel = null;
|
||||
|
||||
const { screenHeight, screenWidth } = uni.getSystemInfoSync();
|
||||
|
||||
this.modalPaddingTop = 12; // modal顶部的内边距
|
||||
this.titleHeight = 34; // 标题的高度
|
||||
this.contentHeight = 60; // 内容得高度
|
||||
this.contentPaddingBottom = 10; // 内容的底部内边距
|
||||
this.footerHeight = 50; // 底部按钮的高度
|
||||
|
||||
const modalHeight = this.modalPaddingTop + this.contentPaddingBottom + this.titleHeight + this.contentHeight + this.footerHeight; // 模态框内容高度
|
||||
|
||||
this.screenHeight = screenHeight;
|
||||
this.modalWidth = options.contentWidth || screenWidth * 0.82; // 模态框内容宽度
|
||||
this.modalHeight = modalHeight; // 模态框内容高度
|
||||
this.modalLeft = (screenWidth - this.modalWidth) / 2; // 模态框距离左边距离
|
||||
this.modalTop = (screenHeight / 2) - (modalHeight / 2) - 30; // 模态框距离顶部距离
|
||||
this.titleTop = this.modalPaddingTop + this.modalTop; // title距离顶部的距离
|
||||
this.contentTop = this.modalPaddingTop + this.modalTop + this.titleHeight; // content距离顶部的距离
|
||||
this.contentLeft = this.modalLeft + (this.modalWidth * 0.1);
|
||||
this.contentWidth = this.modalWidth * 0.8; // 内容的宽度
|
||||
this.footerBorderTop = this.contentPaddingBottom + this.contentTop + this.contentHeight; // footer的边线距离顶部的距离
|
||||
this.buttonWidth = this.modalWidth/2;
|
||||
|
||||
// 物理返回键是否关闭弹框
|
||||
this.backbutton = Boolean(options.backbutton);
|
||||
|
||||
let opacity = options.opacity || 0.6; // mask透明度
|
||||
let modal_title = options.title || '提示'; // 标题
|
||||
let model_content = options.content || '提示内容'; // 提示内容
|
||||
let maskClick = typeof options.maskClick === 'undefined' ? false : options.maskClick; // 是否可以点击mask关闭模态框
|
||||
let cancelText = options.cancelText || '取消';
|
||||
let confirmText = options.confirmText || '确定';
|
||||
let cancelColor = options.cancelColor || '#000000';
|
||||
let confirmColor = options.confirmColor || '#3CC51F';
|
||||
let showCancel = typeof options.showCancel === 'undefined' ? true : options.showCancel; // 是否显示取消按钮
|
||||
let align = options.align || 'center'; // 内容对齐方向
|
||||
let successFn = () => {};
|
||||
let failFn = () => {};
|
||||
this.success = options.success || successFn; // 成功返回模态框
|
||||
this.fail = options.fail || failFn; // 失败返回模态框
|
||||
|
||||
//#ifdef APP-PLUS
|
||||
this.creatView({ height: `${this.screenHeight}px`, top: 0 }, opacity, maskClick, { 'title': modal_title, 'content': model_content, cancelText, confirmText, confirmColor, cancelColor, showCancel, align });
|
||||
//#endif
|
||||
}
|
||||
//生成提示框view
|
||||
creatView(style, opa, maskClick, modelInfo) {
|
||||
try {
|
||||
style = { left: '0px', width: '100%', ...style };
|
||||
let view = new plus.nativeObj.View('FyShowModalView', style);
|
||||
view.draw([
|
||||
{ tag: 'rect', id: 'modal', color: `rgba(0,0,0,${opa})`, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||
{ tag: 'rect', id: 'content', color: `rgb(255,255,255)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: this.modalTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: this.modalHeight + 'px' } },
|
||||
|
||||
{ tag: 'font', id: 'title', text: modelInfo.title, textStyles: { size: '18px', weight: 'bold', color: '#000000' }, position: { top: this.titleTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: this.titleHeight+'px' } },
|
||||
|
||||
{ tag: 'font', id: 'text', text: modelInfo.content, textStyles: { size: '15px', color: '#666', whiteSpace: 'normal', align: modelInfo.align }, position: { top: this.contentTop+'px', left: this.contentLeft+'px', width: this.contentWidth+'px', height: this.contentHeight+'px' } },
|
||||
|
||||
{ tag: 'rect', id: 'line', color: '#efeff1', position: { top: this.footerBorderTop+'px', left: this.modalLeft+'px', width: this.modalWidth+'px', height: '1px' } },
|
||||
{ tag: 'rect', id: 'line2', color: '#efeff1', position: { top: this.footerBorderTop+'px', left: '50%', width: modelInfo.showCancel ? '1px' : '0px', height: modelInfo.showCancel ? this.footerHeight+'px' : '0px' } }
|
||||
]);
|
||||
|
||||
// 取消按钮
|
||||
if (modelInfo.showCancel) {
|
||||
let viewCancel = new plus.nativeObj.View('FyShowModalCancel', { width: this.buttonWidth+'px', height: this.footerHeight+'px', top: this.footerBorderTop + 'px', left: this.modalLeft+'px' });
|
||||
viewCancel.draw([
|
||||
{ tag: 'rect', id: 'cancelBackground', color: `rgba(255,255,255,0)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||
{ tag: 'font', id: 'cancel', text: modelInfo.cancelText, textStyles: { color: modelInfo.cancelColor, size: '16px' } },
|
||||
]);
|
||||
viewCancel.addEventListener('click', (e) => {
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
viewCancel.drawRect('#efeff1', {top:'0px',left:'0px',width:'100%',height:'100%'}, 'cancelBackground');
|
||||
this.success({ confirm: false, cancel: true, mask: false })
|
||||
this.hide();
|
||||
}, false);
|
||||
viewCancel.addEventListener('touchstart', (e)=>{
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
viewCancel.drawRect({ color: '#efeff1', borderWidth: '0px', radius: '8px' }, {top:'0px',left:'0px',width:'100%',height:'100%'}, 'cancelBackground');
|
||||
})
|
||||
this.cancelModel = viewCancel;
|
||||
}
|
||||
|
||||
// 确认
|
||||
let viewconfirm = new plus.nativeObj.View('FyShowModalConfirm', { width: modelInfo.showCancel ? this.buttonWidth+'px' : this.modalWidth+'px', height: this.footerHeight+'px', top: this.footerBorderTop + 'px', left: modelInfo.showCancel ? '50%' : this.modalLeft+'px' });
|
||||
// 绘制确认
|
||||
viewconfirm.draw([
|
||||
{ tag: 'rect', id: 'confirmBackground', color: `rgba(255,255,255,0)`, rectStyles: { borderWidth: '0px', radius: '8px' }, position: { top: '0px', left: '0px', width: '100%', height: '100%' } },
|
||||
{ tag: 'font', id: 'confirm', text: modelInfo.confirmText, textStyles: { color: modelInfo.confirmColor, size: '16px' } },
|
||||
]);
|
||||
// 点击确认
|
||||
viewconfirm.addEventListener('click', (e) => {
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect('#efeff1', {top:'0px',left:'0px',width:'100%',height:'100%'}, 'confirmBackground');
|
||||
this.success({ confirm: true, cancel: false, mask: false })
|
||||
this.hide();
|
||||
}, false);
|
||||
viewconfirm.addEventListener('touchstart', (e)=>{
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect({ color: '#efeff1', borderWidth: '0px', radius: '8px' }, {top:'0px',left:'0px',width:'100%',height:'100%'}, 'confirmBackground');
|
||||
})
|
||||
|
||||
//点击蒙布
|
||||
if (maskClick) {
|
||||
view.addEventListener('click', (e) => {
|
||||
this.success({ confirm: false, cancel: true, mask: true })
|
||||
this.hide();
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
}, false);
|
||||
} else {
|
||||
view.addEventListener('click', (e) => {
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'cancelBackground');
|
||||
}
|
||||
viewconfirm.drawRect({ color: 'rgba(255,255,255,0)', borderWidth: '0px', radius: '8px' }, {}, 'confirmBackground');
|
||||
}, false);
|
||||
}
|
||||
this.modalControl = view;
|
||||
this.confirmModel = viewconfirm;
|
||||
} catch(err) {
|
||||
this.fail(err);
|
||||
}
|
||||
}
|
||||
// 显示模态框
|
||||
show() {
|
||||
this.modalControl.show();
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.show();
|
||||
}
|
||||
this.confirmModel.show();
|
||||
|
||||
if (this.backbutton) {
|
||||
plus.key.addEventListener('backbutton', this.handlerBackButton);
|
||||
}
|
||||
}
|
||||
// 关闭模态框
|
||||
hide() {
|
||||
if (this.backbutton) {
|
||||
plus.key.removeEventListener('backbutton', this.handlerBackButton);
|
||||
}
|
||||
this.modalControl.clear();
|
||||
if (this.cancelModel) {
|
||||
this.cancelModel.clear();
|
||||
}
|
||||
this.confirmModel.clear();
|
||||
}
|
||||
// 物理返回键方法
|
||||
handlerBackButton() {
|
||||
try {
|
||||
modalIntance && modalIntance.success({ confirm: false, cancel: true, mask: false })
|
||||
modalIntance && modalIntance.hide();
|
||||
} catch(err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default fyShowModal;
|
||||
@@ -1,99 +0,0 @@
|
||||
import buildURL from '../helpers/buildURL'
|
||||
import buildFullPath from '../core/buildFullPath'
|
||||
import settle from '../core/settle'
|
||||
import { isUndefined } from "../utils"
|
||||
|
||||
/**
|
||||
* 返回可选值存在的配置
|
||||
* @param {Array} keys - 可选值数组
|
||||
* @param {Object} config2 - 配置
|
||||
* @return {{}} - 存在的配置项
|
||||
*/
|
||||
const mergeKeys = (keys, config2) => {
|
||||
let config = {}
|
||||
keys.forEach(prop => {
|
||||
if (!isUndefined(config2[prop])) {
|
||||
config[prop] = config2[prop]
|
||||
}
|
||||
})
|
||||
return config
|
||||
}
|
||||
export default (config) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let fullPath = buildURL(buildFullPath(config.baseURL, config.url), config.params)
|
||||
const _config = {
|
||||
url: fullPath,
|
||||
header: config.header,
|
||||
complete: (response) => {
|
||||
config.fullPath = fullPath
|
||||
response.config = config
|
||||
try {
|
||||
// 对可能字符串不是json 的情况容错
|
||||
if (typeof response.data === 'string') {
|
||||
response.data = JSON.parse(response.data)
|
||||
}
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {
|
||||
}
|
||||
settle(resolve, reject, response)
|
||||
}
|
||||
}
|
||||
let requestTask
|
||||
if (config.method === 'UPLOAD') {
|
||||
delete _config.header['content-type']
|
||||
delete _config.header['Content-Type']
|
||||
let otherConfig = {
|
||||
// #ifdef MP-ALIPAY
|
||||
fileType: config.fileType,
|
||||
// #endif
|
||||
filePath: config.filePath,
|
||||
name: config.name
|
||||
}
|
||||
const optionalKeys = [
|
||||
// #ifdef APP-PLUS || H5
|
||||
'files',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'file',
|
||||
// #endif
|
||||
// #ifdef H5 || APP-PLUS
|
||||
'timeout',
|
||||
// #endif
|
||||
'formData'
|
||||
]
|
||||
requestTask = uni.uploadFile({..._config, ...otherConfig, ...mergeKeys(optionalKeys, config)})
|
||||
} else if (config.method === 'DOWNLOAD') {
|
||||
// #ifdef H5 || APP-PLUS
|
||||
if (!isUndefined(config['timeout'])) {
|
||||
_config['timeout'] = config['timeout']
|
||||
}
|
||||
// #endif
|
||||
requestTask = uni.downloadFile(_config)
|
||||
} else {
|
||||
const optionalKeys = [
|
||||
'data',
|
||||
'method',
|
||||
// #ifdef H5 || APP-PLUS || MP-ALIPAY || MP-WEIXIN
|
||||
'timeout',
|
||||
// #endif
|
||||
'dataType',
|
||||
// #ifndef MP-ALIPAY
|
||||
'responseType',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'sslVerify',
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
'withCredentials',
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
'firstIpv4',
|
||||
// #endif
|
||||
]
|
||||
requestTask = uni.request({..._config,...mergeKeys(optionalKeys, config)})
|
||||
}
|
||||
if (config.getTask) {
|
||||
config.getTask(requestTask, config)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
|
||||
function InterceptorManager() {
|
||||
this.handlers = []
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new interceptor to the stack
|
||||
*
|
||||
* @param {Function} fulfilled The function to handle `then` for a `Promise`
|
||||
* @param {Function} rejected The function to handle `reject` for a `Promise`
|
||||
*
|
||||
* @return {Number} An ID used to remove interceptor later
|
||||
*/
|
||||
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
|
||||
this.handlers.push({
|
||||
fulfilled: fulfilled,
|
||||
rejected: rejected
|
||||
})
|
||||
return this.handlers.length - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an interceptor from the stack
|
||||
*
|
||||
* @param {Number} id The ID that was returned by `use`
|
||||
*/
|
||||
InterceptorManager.prototype.eject = function eject(id) {
|
||||
if (this.handlers[id]) {
|
||||
this.handlers[id] = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all the registered interceptors
|
||||
*
|
||||
* This method is particularly useful for skipping over any
|
||||
* interceptors that may have become `null` calling `eject`.
|
||||
*
|
||||
* @param {Function} fn The function to call for each interceptor
|
||||
*/
|
||||
InterceptorManager.prototype.forEach = function forEach(fn) {
|
||||
this.handlers.forEach(h => {
|
||||
if (h !== null) {
|
||||
fn(h)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export default InterceptorManager
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user