diff --git a/PINIA_GUIDE.md b/PINIA_GUIDE.md new file mode 100644 index 0000000..88371de --- /dev/null +++ b/PINIA_GUIDE.md @@ -0,0 +1,165 @@ +# Pinia 状态管理使用指南 + +## 概述 + +项目已集成 Pinia 状态管理库和 `pinia-plugin-persistedstate` 插件,支持自动将状态持久化到 localStorage。 + +## 安装的依赖 + +- `pinia`: Vue 官方状态管理库 +- `pinia-plugin-persistedstate`: Pinia 持久化插件 + +## 目录结构 + +``` +src/ +├── stores/ +│ ├── modules/ # 模块化 stores 目录 +│ │ ├── counter.ts # 计数器模块 +│ │ ├── user.ts # 用户信息模块 +│ │ ├── preferences.ts # 偏好设置模块 +│ │ └── example.ts # 综合示例模块 +│ └── index.ts # 统一导出文件 +└── main.ts # Pinia 配置 +``` + +## 基本用法 + +### 1. 创建 Store + +在 `src/stores/` 目录下创建新的 store 文件: + +```typescript +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useMyStore = defineStore( + 'myStore', // store id,必须唯一 + () => { + // 使用 Composition API 风格 + const data = ref('') + + function updateData(newData: string): void { + data.value = newData + } + + return { + data, + updateData + } + }, + { + // 启用持久化到 localStorage + persist: true + } +) +``` + +### 2. 在组件中使用 Store + +```vue + + + +``` + +### 3. 高级持久化配置 + +如果只想持久化部分状态: + +```typescript +export const useMyStore = defineStore( + 'myStore', + () => { + const persistedData = ref('') + const temporaryData = ref('') + + return { + persistedData, + temporaryData + } + }, + { + persist: { + // 自定义 localStorage key + key: 'my-custom-key', + // 只持久化指定的状态 + paths: ['persistedData'], + // 自定义存储方式(默认是 localStorage) + storage: localStorage + } + } +) +``` + +## Store 模块化设计 + +项目采用**模块化**设计,将不同职责的状态拆分到独立的 stores: + +### 已创建的模块化 Stores + +#### 1. Counter Store (`src/stores/modules/counter.ts`) + +- 管理计数器状态 +- 提供 increment, decrement, reset 方法 + +#### 2. User Store (`src/stores/modules/user.ts`) + +- 管理用户基本信息 +- 提供 setUserName, clearUserName 方法 + +#### 3. Preferences Store (`src/stores/modules/preferences.ts`) + +- 管理用户偏好设置(主题、语言等) +- 包含 computed getters (isDarkMode, currentLanguage) +- 提供主题切换、语言设置等方法 + +#### 4. Example Store (`src/stores/modules/example.ts`) + +- 综合示例,展示所有功能 +- 可作为参考模板 + +### 模块化优势 + +- ✅ **职责单一**: 每个 store 只管理特定领域的状态 +- ✅ **易于维护**: 代码结构清晰,便于定位和修改 +- ✅ **可复用**: 小模块更容易在不同场景复用 +- ✅ **按需加载**: 组件只导入需要的 stores +- ✅ **类型安全**: 每个模块有独立的类型定义 + +## 持久化特性 + +- 🔄 **自动保存**: 状态变化时自动保存到 localStorage +- 🚀 **自动恢复**: 页面刷新后自动从 localStorage 恢复状态 +- 🎯 **类型安全**: 完整的 TypeScript 支持 +- ⚙️ **可配置**: 支持自定义持久化策略 + +## 注意事项 + +1. **Store ID 必须唯一**: 每个 store 的第一个参数(store id)必须在应用中唯一 +2. **localStorage 限制**: 浏览器 localStorage 通常有 5-10MB 的存储限制 +3. **敏感数据**: 不要在 localStorage 中存储敏感信息(如密码、token 等) +4. **序列化限制**: 只能存储可序列化的数据(不支持 Function, Symbol 等) + +## 参考资源 + +- [Pinia 官方文档](https://pinia.vuejs.org/) +- [pinia-plugin-persistedstate 文档](https://prazdevs.github.io/pinia-plugin-persistedstate/) diff --git a/eslint.config.ts b/eslint.config.ts index b539dfd..fb2fb92 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -6,12 +6,23 @@ import { defineConfig } from 'eslint/config' export default defineConfig([ { files: ['**/*.{js,mjs,cjs,ts,mts,cts,vue}'], languageOptions: { globals: globals.browser } }, tseslint.configs.recommended, + { + rules: { + '@typescript-eslint/no-explicit-any': 'off' + } + }, pluginVue.configs['flat/essential'], { files: ['**/*.vue'], languageOptions: { parserOptions: { parser: tseslint.parser } }, rules: { 'vue/multi-word-component-names': 'off', + 'vue/no-undef-components': [ + 'error', + { + ignorePatterns: ['router-view', 'router-link', 'RouterView', 'RouterLink'] + } + ], '@typescript-eslint/no-unused-vars': 'warn', '@typescript-eslint/no-explicit-any': 'off', // 完全禁用 any 检查 // 关闭所有需要类型信息的规则 diff --git a/index.html b/index.html index dde16aa..1f36676 100644 --- a/index.html +++ b/index.html @@ -1,10 +1,14 @@ - + - - Vite + Vue + TS + + + 短视频达人
diff --git a/package.json b/package.json index c13b9fb..1865b89 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,11 @@ "prepare": "husky" }, "dependencies": { + "@zumer/snapdom": "^2.0.0", + "html2canvas": "^1.4.1", "less": "^4.4.1", + "pinia": "^3.0.4", + "pinia-plugin-persistedstate": "^4.7.1", "vue": "^3.5.18", "vue-router": "^4.5.1" }, @@ -23,6 +27,7 @@ "@vitejs/plugin-vue": "^6.0.1", "@vue/tsconfig": "^0.7.0", "autoprefixer": "^10.4.21", + "baseline-browser-mapping": "^2.8.28", "eslint": "^9.35.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7a285cc..834f685 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,21 @@ importers: .: dependencies: + '@zumer/snapdom': + specifier: ^2.0.0 + version: 2.0.0 + html2canvas: + specifier: ^1.4.1 + version: 1.4.1 less: specifier: ^4.4.1 version: 4.4.1 + pinia: + specifier: ^3.0.4 + version: 3.0.4(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3)) + pinia-plugin-persistedstate: + specifier: ^4.7.1 + version: 4.7.1(pinia@3.0.4(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3))) vue: specifier: ^3.5.18 version: 3.5.21(typescript@5.8.3) @@ -36,6 +48,9 @@ importers: autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) + baseline-browser-mapping: + specifier: ^2.8.28 + version: 2.8.28 eslint: specifier: ^9.35.0 version: 9.35.0(jiti@2.5.1) @@ -649,6 +664,15 @@ packages: '@vue/devtools-api@6.6.4': resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + '@vue/devtools-api@7.7.9': + resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + + '@vue/devtools-kit@7.7.9': + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + + '@vue/devtools-shared@7.7.9': + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + '@vue/language-core@3.0.7': resolution: {integrity: sha512-0sqqyqJ0Gn33JH3TdIsZLCZZ8Gr4kwlg8iYOnOrDDkJKSjFurlQY/bEFQx5zs7SX2C/bjMkmPYq/NiyY1fTOkw==} peerDependencies: @@ -685,6 +709,9 @@ packages: vue: optional: true + '@zumer/snapdom@2.0.0': + resolution: {integrity: sha512-e/fkm5wCUd+9CssUIyH09xTeR4DvRTmZLGVOlnXLhr4HeI7sdc6ed8cLPiZKFtiQDRiwD3EKx4RIUrpQOJQY7A==} + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -754,14 +781,21 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.8.2: - resolution: {integrity: sha512-NvcIedLxrs9llVpX7wI+Jz4Hn9vJQkCPKrTaHIE0sW/Rj1iq6Fzby4NbyTZjQJNoypBXNaG7tEHkTgONZpwgxQ==} + base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + + baseline-browser-mapping@2.8.28: + resolution: {integrity: sha512-gYjt7OIqdM0PcttNYP2aVrr2G0bMALkBaoehD4BuRGjAOtipg0b6wHg1yNL+s5zSnLZZrGHOw4IrND8CD+3oIQ==} hasBin: true binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -855,6 +889,10 @@ packages: copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + cosmiconfig-typescript-loader@6.1.0: resolution: {integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==} engines: {node: '>=v18'} @@ -876,6 +914,9 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -903,6 +944,9 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + detect-libc@2.0.4: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} @@ -1167,6 +1211,13 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} @@ -1250,6 +1301,10 @@ packages: is-what@3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1470,6 +1525,9 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1579,6 +1637,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1603,6 +1664,29 @@ packages: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} + pinia-plugin-persistedstate@4.7.1: + resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==} + peerDependencies: + '@nuxt/kit': '>=3.0.0' + '@pinia/nuxt': '>=0.10.0' + pinia: '>=3.0.0' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@pinia/nuxt': + optional: true + pinia: + optional: true + + pinia@3.0.4: + resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} + peerDependencies: + typescript: '>=4.5.0' + vue: ^3.5.11 + peerDependenciesMeta: + typescript: + optional: true + pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} @@ -1761,6 +1845,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -1798,6 +1886,10 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + engines: {node: '>=16'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1819,6 +1911,9 @@ packages: resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} engines: {node: '>=8'} + text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -1887,6 +1982,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + vite@7.1.5: resolution: {integrity: sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2536,6 +2634,24 @@ snapshots: '@vue/devtools-api@6.6.4': {} + '@vue/devtools-api@7.7.9': + dependencies: + '@vue/devtools-kit': 7.7.9 + + '@vue/devtools-kit@7.7.9': + dependencies: + '@vue/devtools-shared': 7.7.9 + birpc: 2.9.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.6 + + '@vue/devtools-shared@7.7.9': + dependencies: + rfdc: 1.4.1 + '@vue/language-core@3.0.7(typescript@5.8.3)': dependencies: '@volar/language-core': 2.4.23 @@ -2578,6 +2694,8 @@ snapshots: typescript: 5.8.3 vue: 3.5.21(typescript@5.8.3) + '@zumer/snapdom@2.0.0': {} + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -2644,10 +2762,14 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.8.2: {} + base64-arraybuffer@1.0.2: {} + + baseline-browser-mapping@2.8.28: {} binary-extensions@2.3.0: {} + birpc@2.9.0: {} + boolbase@1.0.0: {} brace-expansion@1.1.12: @@ -2665,7 +2787,7 @@ snapshots: browserslist@4.26.0: dependencies: - baseline-browser-mapping: 2.8.2 + baseline-browser-mapping: 2.8.28 caniuse-lite: 1.0.30001741 electron-to-chromium: 1.5.218 node-releases: 2.0.21 @@ -2749,6 +2871,10 @@ snapshots: dependencies: is-what: 3.14.1 + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + cosmiconfig-typescript-loader@6.1.0(@types/node@24.3.3)(cosmiconfig@9.0.0(typescript@5.8.3))(typescript@5.8.3): dependencies: '@types/node': 24.3.3 @@ -2771,6 +2897,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-line-break@2.1.0: + dependencies: + utrie: 1.0.2 + cssesc@3.0.0: {} csstype@3.1.3: {} @@ -2785,6 +2915,8 @@ snapshots: deep-is@0.1.4: {} + defu@6.1.4: {} + detect-libc@2.0.4: optional: true @@ -3065,6 +3197,13 @@ snapshots: he@1.2.0: {} + hookable@5.5.3: {} + + html2canvas@1.4.1: + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + husky@9.1.7: {} iconv-lite@0.6.3: @@ -3124,6 +3263,8 @@ snapshots: is-what@3.14.1: {} + is-what@5.5.0: {} + isexe@2.0.0: {} jackspeak@3.4.3: @@ -3323,6 +3464,8 @@ snapshots: minipass@7.1.2: {} + mitt@3.0.1: {} + ms@2.1.3: {} muggle-string@0.4.1: {} @@ -3418,6 +3561,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 + perfect-debounce@1.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3431,6 +3576,19 @@ snapshots: pify@4.0.1: optional: true + pinia-plugin-persistedstate@4.7.1(pinia@3.0.4(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3))): + dependencies: + defu: 6.1.4 + optionalDependencies: + pinia: 3.0.4(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3)) + + pinia@3.0.4(typescript@5.8.3)(vue@3.5.21(typescript@5.8.3)): + dependencies: + '@vue/devtools-api': 7.7.9 + vue: 3.5.21(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 + pirates@4.0.7: {} postcss-import@15.1.0(postcss@8.5.6): @@ -3581,6 +3739,8 @@ snapshots: source-map@0.6.1: optional: true + speakingurl@14.0.1: {} + split2@4.2.0: {} string-argv@0.3.2: {} @@ -3623,6 +3783,10 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 + superjson@2.2.6: + dependencies: + copy-anything: 4.0.5 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -3662,6 +3826,10 @@ snapshots: text-extensions@2.4.0: {} + text-segmentation@1.0.3: + dependencies: + utrie: 1.0.2 + thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -3724,6 +3892,10 @@ snapshots: util-deprecate@1.0.2: {} + utrie@1.0.2: + dependencies: + base64-arraybuffer: 1.0.2 + vite@7.1.5(@types/node@24.3.3)(jiti@2.5.1)(less@4.4.1)(lightningcss@1.30.1)(yaml@2.8.1): dependencies: esbuild: 0.25.9 diff --git a/src/App.vue b/src/App.vue index 06916d3..6d0f9ee 100644 --- a/src/App.vue +++ b/src/App.vue @@ -2,7 +2,7 @@
- + diff --git a/src/assets/images/after-editing/color/film_img.png b/src/assets/images/after-editing/color/film_img.png new file mode 100644 index 0000000..0050700 Binary files /dev/null and b/src/assets/images/after-editing/color/film_img.png differ diff --git a/src/assets/images/afterditing/color/冬日物语.jpg b/src/assets/images/after-editing/color/冬日物语.jpg similarity index 100% rename from src/assets/images/afterditing/color/冬日物语.jpg rename to src/assets/images/after-editing/color/冬日物语.jpg diff --git a/src/assets/images/afterditing/color/卡片-冬日物语.png b/src/assets/images/after-editing/color/卡片-冬日物语.png similarity index 100% rename from src/assets/images/afterditing/color/卡片-冬日物语.png rename to src/assets/images/after-editing/color/卡片-冬日物语.png diff --git a/src/assets/images/afterditing/color/卡片-日落增色.png b/src/assets/images/after-editing/color/卡片-日落增色.png similarity index 100% rename from src/assets/images/afterditing/color/卡片-日落增色.png rename to src/assets/images/after-editing/color/卡片-日落增色.png diff --git a/src/assets/images/afterditing/color/卡片-智能调色.png b/src/assets/images/after-editing/color/卡片-智能调色.png similarity index 100% rename from src/assets/images/afterditing/color/卡片-智能调色.png rename to src/assets/images/after-editing/color/卡片-智能调色.png diff --git a/src/assets/images/afterditing/color/卡片-落日余晖.png b/src/assets/images/after-editing/color/卡片-落日余晖.png similarity index 100% rename from src/assets/images/afterditing/color/卡片-落日余晖.png rename to src/assets/images/after-editing/color/卡片-落日余晖.png diff --git a/src/assets/images/afterditing/color/卡片-落日火烧云.png b/src/assets/images/after-editing/color/卡片-落日火烧云.png similarity index 100% rename from src/assets/images/afterditing/color/卡片-落日火烧云.png rename to src/assets/images/after-editing/color/卡片-落日火烧云.png diff --git a/src/assets/images/afterditing/color/原图.png b/src/assets/images/after-editing/color/原图.png similarity index 100% rename from src/assets/images/afterditing/color/原图.png rename to src/assets/images/after-editing/color/原图.png diff --git a/src/assets/images/after-editing/color/图片.png b/src/assets/images/after-editing/color/图片.png new file mode 100644 index 0000000..e8cf546 Binary files /dev/null and b/src/assets/images/after-editing/color/图片.png differ diff --git a/src/assets/images/after-editing/color/图片框.png b/src/assets/images/after-editing/color/图片框.png new file mode 100644 index 0000000..e3a5774 Binary files /dev/null and b/src/assets/images/after-editing/color/图片框.png differ diff --git a/src/assets/images/after-editing/color/小底板.png b/src/assets/images/after-editing/color/小底板.png new file mode 100644 index 0000000..fee8504 Binary files /dev/null and b/src/assets/images/after-editing/color/小底板.png differ diff --git a/src/assets/images/after-editing/color/底图.png b/src/assets/images/after-editing/color/底图.png new file mode 100644 index 0000000..d171452 Binary files /dev/null and b/src/assets/images/after-editing/color/底图.png differ diff --git a/src/assets/images/afterditing/color/开启对比 - 不可开启.png b/src/assets/images/after-editing/color/开启对比 - 不可开启.png similarity index 100% rename from src/assets/images/afterditing/color/开启对比 - 不可开启.png rename to src/assets/images/after-editing/color/开启对比 - 不可开启.png diff --git a/src/assets/images/afterditing/color/开启对比 - 可开启.png b/src/assets/images/after-editing/color/开启对比 - 可开启.png similarity index 100% rename from src/assets/images/afterditing/color/开启对比 - 可开启.png rename to src/assets/images/after-editing/color/开启对比 - 可开启.png diff --git a/src/assets/images/afterditing/color/按钮-保存.png b/src/assets/images/after-editing/color/按钮-保存.png similarity index 100% rename from src/assets/images/afterditing/color/按钮-保存.png rename to src/assets/images/after-editing/color/按钮-保存.png diff --git a/src/assets/images/afterditing/color/按钮-结束调色实践.png b/src/assets/images/after-editing/color/按钮-结束调色实践.png similarity index 100% rename from src/assets/images/afterditing/color/按钮-结束调色实践.png rename to src/assets/images/after-editing/color/按钮-结束调色实践.png diff --git a/src/assets/images/afterditing/color/按钮-试试其他调色.png b/src/assets/images/after-editing/color/按钮-试试其他调色.png similarity index 100% rename from src/assets/images/afterditing/color/按钮-试试其他调色.png rename to src/assets/images/after-editing/color/按钮-试试其他调色.png diff --git a/src/assets/images/afterditing/color/按钮-调色未选中.png b/src/assets/images/after-editing/color/按钮-调色未选中.png similarity index 100% rename from src/assets/images/afterditing/color/按钮-调色未选中.png rename to src/assets/images/after-editing/color/按钮-调色未选中.png diff --git a/src/assets/images/afterditing/color/按钮-调色选中.png b/src/assets/images/after-editing/color/按钮-调色选中.png similarity index 100% rename from src/assets/images/afterditing/color/按钮-调色选中.png rename to src/assets/images/after-editing/color/按钮-调色选中.png diff --git a/src/assets/images/after-editing/color/效果图.png b/src/assets/images/after-editing/color/效果图.png new file mode 100644 index 0000000..34a0af5 Binary files /dev/null and b/src/assets/images/after-editing/color/效果图.png differ diff --git a/src/assets/images/afterditing/color/日落增色.png b/src/assets/images/after-editing/color/日落增色.png similarity index 100% rename from src/assets/images/afterditing/color/日落增色.png rename to src/assets/images/after-editing/color/日落增色.png diff --git a/src/assets/images/afterditing/color/智能调色.png b/src/assets/images/after-editing/color/智能调色.png similarity index 100% rename from src/assets/images/afterditing/color/智能调色.png rename to src/assets/images/after-editing/color/智能调色.png diff --git a/src/assets/images/afterditing/color/标题-调色.png b/src/assets/images/after-editing/color/标题-调色.png similarity index 100% rename from src/assets/images/afterditing/color/标题-调色.png rename to src/assets/images/after-editing/color/标题-调色.png diff --git a/src/assets/images/afterditing/color/标题-调色实践.png b/src/assets/images/after-editing/color/标题-调色实践.png similarity index 100% rename from src/assets/images/afterditing/color/标题-调色实践.png rename to src/assets/images/after-editing/color/标题-调色实践.png diff --git a/src/assets/images/after-editing/color/滑块.png b/src/assets/images/after-editing/color/滑块.png new file mode 100644 index 0000000..52cd9be Binary files /dev/null and b/src/assets/images/after-editing/color/滑块.png differ diff --git a/src/assets/images/after-editing/color/滤镜-亮.png b/src/assets/images/after-editing/color/滤镜-亮.png new file mode 100644 index 0000000..aa10b46 Binary files /dev/null and b/src/assets/images/after-editing/color/滤镜-亮.png differ diff --git a/src/assets/images/after-editing/color/滤镜-暗.png b/src/assets/images/after-editing/color/滤镜-暗.png new file mode 100644 index 0000000..81b16d2 Binary files /dev/null and b/src/assets/images/after-editing/color/滤镜-暗.png differ diff --git a/src/assets/images/after-editing/color/滤镜-未选中.png b/src/assets/images/after-editing/color/滤镜-未选中.png new file mode 100644 index 0000000..5c82e97 Binary files /dev/null and b/src/assets/images/after-editing/color/滤镜-未选中.png differ diff --git a/src/assets/images/after-editing/color/滤镜-选中.png b/src/assets/images/after-editing/color/滤镜-选中.png new file mode 100644 index 0000000..0427763 Binary files /dev/null and b/src/assets/images/after-editing/color/滤镜-选中.png differ diff --git a/src/assets/images/after-editing/color/生成.png b/src/assets/images/after-editing/color/生成.png new file mode 100644 index 0000000..9fd7e2a Binary files /dev/null and b/src/assets/images/after-editing/color/生成.png differ diff --git a/src/assets/images/afterditing/color/落日余晖.jpg b/src/assets/images/after-editing/color/落日余晖.jpg similarity index 100% rename from src/assets/images/afterditing/color/落日余晖.jpg rename to src/assets/images/after-editing/color/落日余晖.jpg diff --git a/src/assets/images/afterditing/color/落日火烧云.png b/src/assets/images/after-editing/color/落日火烧云.png similarity index 100% rename from src/assets/images/afterditing/color/落日火烧云.png rename to src/assets/images/after-editing/color/落日火烧云.png diff --git a/src/assets/images/after-editing/color/调色-亮.png b/src/assets/images/after-editing/color/调色-亮.png new file mode 100644 index 0000000..f970f80 Binary files /dev/null and b/src/assets/images/after-editing/color/调色-亮.png differ diff --git a/src/assets/images/after-editing/color/调色-暗.png b/src/assets/images/after-editing/color/调色-暗.png new file mode 100644 index 0000000..09f14d2 Binary files /dev/null and b/src/assets/images/after-editing/color/调色-暗.png differ diff --git a/src/assets/images/afterditing/color/调色实践.png b/src/assets/images/after-editing/color/调色实践.png similarity index 100% rename from src/assets/images/afterditing/color/调色实践.png rename to src/assets/images/after-editing/color/调色实践.png diff --git a/src/assets/images/after-editing/color/调色效果图.png b/src/assets/images/after-editing/color/调色效果图.png new file mode 100644 index 0000000..7d9e6b5 Binary files /dev/null and b/src/assets/images/after-editing/color/调色效果图.png differ diff --git a/src/assets/images/after-editing/color/重置.png b/src/assets/images/after-editing/color/重置.png new file mode 100644 index 0000000..fea2eb6 Binary files /dev/null and b/src/assets/images/after-editing/color/重置.png differ diff --git a/src/assets/images/afterditing/后期剪辑.png b/src/assets/images/afterditing/后期剪辑.png deleted file mode 100644 index c7fed27..0000000 Binary files a/src/assets/images/afterditing/后期剪辑.png and /dev/null differ diff --git a/src/assets/images/afterditing/后期剪辑底板.png b/src/assets/images/afterditing/后期剪辑底板.png deleted file mode 100644 index e5b8239..0000000 Binary files a/src/assets/images/afterditing/后期剪辑底板.png and /dev/null differ diff --git a/src/assets/images/afterditing/按钮-剪辑.png b/src/assets/images/afterditing/按钮-剪辑.png deleted file mode 100644 index ec5d116..0000000 Binary files a/src/assets/images/afterditing/按钮-剪辑.png and /dev/null differ diff --git a/src/assets/images/afterditing/按钮-调色.png b/src/assets/images/afterditing/按钮-调色.png deleted file mode 100644 index 3da1030..0000000 Binary files a/src/assets/images/afterditing/按钮-调色.png and /dev/null differ diff --git a/src/assets/images/afterditing/按钮选中-剪辑.png b/src/assets/images/afterditing/按钮选中-剪辑.png deleted file mode 100644 index d5a1373..0000000 Binary files a/src/assets/images/afterditing/按钮选中-剪辑.png and /dev/null differ diff --git a/src/assets/images/components/long.png b/src/assets/images/components/long.png new file mode 100644 index 0000000..9d7d8a8 Binary files /dev/null and b/src/assets/images/components/long.png differ diff --git a/src/assets/images/components/menu-bg.png b/src/assets/images/components/menu-bg.png new file mode 100644 index 0000000..a918a16 Binary files /dev/null and b/src/assets/images/components/menu-bg.png differ diff --git a/src/assets/images/components/menu-hover.png b/src/assets/images/components/menu-hover.png new file mode 100644 index 0000000..34fa753 Binary files /dev/null and b/src/assets/images/components/menu-hover.png differ diff --git a/src/assets/images/equipment/标题-安卓小米.png b/src/assets/images/equipment/标题-安卓小米.png index 80a8350..e70e94d 100644 Binary files a/src/assets/images/equipment/标题-安卓小米.png and b/src/assets/images/equipment/标题-安卓小米.png differ diff --git a/src/assets/images/equipment/标题-苹果.png b/src/assets/images/equipment/标题-苹果.png index 8cd403c..b2a005a 100644 Binary files a/src/assets/images/equipment/标题-苹果.png and b/src/assets/images/equipment/标题-苹果.png differ diff --git a/src/assets/images/equipment/设备-手机1.png b/src/assets/images/equipment/设备-手机1.png index bd2243c..940e758 100644 Binary files a/src/assets/images/equipment/设备-手机1.png and b/src/assets/images/equipment/设备-手机1.png differ diff --git a/src/assets/images/equipment/设备-手机2.png b/src/assets/images/equipment/设备-手机2.png index 49839e1..9d9d069 100644 Binary files a/src/assets/images/equipment/设备-手机2.png and b/src/assets/images/equipment/设备-手机2.png differ diff --git a/src/assets/images/film/equipment/微单产品.png b/src/assets/images/film/equipment/微单产品.png new file mode 100644 index 0000000..263d729 Binary files /dev/null and b/src/assets/images/film/equipment/微单产品.png differ diff --git a/src/assets/images/film/equipment/摄像机产品.png b/src/assets/images/film/equipment/摄像机产品.png new file mode 100644 index 0000000..406ca03 Binary files /dev/null and b/src/assets/images/film/equipment/摄像机产品.png differ diff --git a/src/assets/images/film/equipment/相机产品.png b/src/assets/images/film/equipment/相机产品.png new file mode 100644 index 0000000..6ce92cb Binary files /dev/null and b/src/assets/images/film/equipment/相机产品.png differ diff --git a/src/assets/images/film/equipment/相机内容.png b/src/assets/images/film/equipment/相机内容.png new file mode 100644 index 0000000..29e0682 Binary files /dev/null and b/src/assets/images/film/equipment/相机内容.png differ diff --git a/src/assets/images/film/equipment/相机标题.png b/src/assets/images/film/equipment/相机标题.png new file mode 100644 index 0000000..76fdfce Binary files /dev/null and b/src/assets/images/film/equipment/相机标题.png differ diff --git a/src/assets/images/film/img/分镜制作-标题.png b/src/assets/images/film/img/分镜制作-标题.png new file mode 100644 index 0000000..d45fd62 Binary files /dev/null and b/src/assets/images/film/img/分镜制作-标题.png differ diff --git a/src/assets/images/film/img/分镜制作.png b/src/assets/images/film/img/分镜制作.png new file mode 100644 index 0000000..9a48ef9 Binary files /dev/null and b/src/assets/images/film/img/分镜制作.png differ diff --git a/src/assets/images/film/img/创意构想-标题.png b/src/assets/images/film/img/创意构想-标题.png new file mode 100644 index 0000000..024949a Binary files /dev/null and b/src/assets/images/film/img/创意构想-标题.png differ diff --git a/src/assets/images/film/img/创意构想.png b/src/assets/images/film/img/创意构想.png new file mode 100644 index 0000000..da5e8c5 Binary files /dev/null and b/src/assets/images/film/img/创意构想.png differ diff --git a/src/assets/images/film/img/背景.png b/src/assets/images/film/img/背景.png new file mode 100644 index 0000000..3cfd0b9 Binary files /dev/null and b/src/assets/images/film/img/背景.png differ diff --git a/src/assets/images/film/img/脚本编写-标题.png b/src/assets/images/film/img/脚本编写-标题.png new file mode 100644 index 0000000..74a8114 Binary files /dev/null and b/src/assets/images/film/img/脚本编写-标题.png differ diff --git a/src/assets/images/film/img/脚本编写.png b/src/assets/images/film/img/脚本编写.png new file mode 100644 index 0000000..a67ae33 Binary files /dev/null and b/src/assets/images/film/img/脚本编写.png differ diff --git a/src/assets/images/film/img/调色-标题.png b/src/assets/images/film/img/调色-标题.png new file mode 100644 index 0000000..f66daf9 Binary files /dev/null and b/src/assets/images/film/img/调色-标题.png differ diff --git a/src/assets/images/film/img/调色.png b/src/assets/images/film/img/调色.png new file mode 100644 index 0000000..bf14f40 Binary files /dev/null and b/src/assets/images/film/img/调色.png differ diff --git a/src/assets/images/film/img/调色插视频示意.png b/src/assets/images/film/img/调色插视频示意.png new file mode 100644 index 0000000..e3bc68d Binary files /dev/null and b/src/assets/images/film/img/调色插视频示意.png differ diff --git a/src/assets/images/film/img/资源筹备-标题.png b/src/assets/images/film/img/资源筹备-标题.png new file mode 100644 index 0000000..52f5617 Binary files /dev/null and b/src/assets/images/film/img/资源筹备-标题.png differ diff --git a/src/assets/images/film/img/资源筹备.png b/src/assets/images/film/img/资源筹备.png new file mode 100644 index 0000000..64dea92 Binary files /dev/null and b/src/assets/images/film/img/资源筹备.png differ diff --git a/src/assets/images/film/img/音效-标题.png b/src/assets/images/film/img/音效-标题.png new file mode 100644 index 0000000..9d795a8 Binary files /dev/null and b/src/assets/images/film/img/音效-标题.png differ diff --git a/src/assets/images/film/img/音效.png b/src/assets/images/film/img/音效.png new file mode 100644 index 0000000..721ceb4 Binary files /dev/null and b/src/assets/images/film/img/音效.png differ diff --git a/src/assets/images/film/img/音效插视频示意.png b/src/assets/images/film/img/音效插视频示意.png new file mode 100644 index 0000000..8340da3 Binary files /dev/null and b/src/assets/images/film/img/音效插视频示意.png differ diff --git a/src/assets/images/film/on-site/低角度拍摄jh_1.png b/src/assets/images/film/on-site/低角度拍摄jh_1.png new file mode 100644 index 0000000..da1d7b7 Binary files /dev/null and b/src/assets/images/film/on-site/低角度拍摄jh_1.png differ diff --git a/src/assets/images/film/on-site/低角度拍摄jh_3.png b/src/assets/images/film/on-site/低角度拍摄jh_3.png new file mode 100644 index 0000000..5b31422 Binary files /dev/null and b/src/assets/images/film/on-site/低角度拍摄jh_3.png differ diff --git a/src/assets/images/film/on-site/声音转场jh_2.png b/src/assets/images/film/on-site/声音转场jh_2.png new file mode 100644 index 0000000..a162458 Binary files /dev/null and b/src/assets/images/film/on-site/声音转场jh_2.png differ diff --git a/src/assets/images/film/on-site/延时拍摄jh_1.png b/src/assets/images/film/on-site/延时拍摄jh_1.png new file mode 100644 index 0000000..35c3417 Binary files /dev/null and b/src/assets/images/film/on-site/延时拍摄jh_1.png differ diff --git a/src/assets/images/film/on-site/白天慢门jh_1.png b/src/assets/images/film/on-site/白天慢门jh_1.png new file mode 100644 index 0000000..53ee012 Binary files /dev/null and b/src/assets/images/film/on-site/白天慢门jh_1.png differ diff --git a/src/assets/images/film/on-site/白天慢门jh_3.png b/src/assets/images/film/on-site/白天慢门jh_3.png new file mode 100644 index 0000000..1018398 Binary files /dev/null and b/src/assets/images/film/on-site/白天慢门jh_3.png differ diff --git a/src/assets/images/film/practice/创作笔记底图-电影感.png b/src/assets/images/film/practice/创作笔记底图-电影感.png new file mode 100644 index 0000000..622206e Binary files /dev/null and b/src/assets/images/film/practice/创作笔记底图-电影感.png differ diff --git a/src/assets/images/film/practice/实战操作台首页- 电影感.png b/src/assets/images/film/practice/实战操作台首页- 电影感.png new file mode 100644 index 0000000..d73f905 Binary files /dev/null and b/src/assets/images/film/practice/实战操作台首页- 电影感.png differ diff --git a/src/assets/images/film/practice/滑动任务面板-电影感.png b/src/assets/images/film/practice/滑动任务面板-电影感.png new file mode 100644 index 0000000..1cfea36 Binary files /dev/null and b/src/assets/images/film/practice/滑动任务面板-电影感.png differ diff --git a/src/assets/images/film/practice/评分底板-电影感.png b/src/assets/images/film/practice/评分底板-电影感.png new file mode 100644 index 0000000..f139c39 Binary files /dev/null and b/src/assets/images/film/practice/评分底板-电影感.png differ diff --git a/src/assets/images/film/shooting/01框架构图_底图.png b/src/assets/images/film/shooting/01框架构图_底图.png new file mode 100644 index 0000000..93a51ce Binary files /dev/null and b/src/assets/images/film/shooting/01框架构图_底图.png differ diff --git a/src/assets/images/film/shooting/01框架构图_覆盖.png b/src/assets/images/film/shooting/01框架构图_覆盖.png new file mode 100644 index 0000000..7cd7cc8 Binary files /dev/null and b/src/assets/images/film/shooting/01框架构图_覆盖.png differ diff --git a/src/assets/images/film/shooting/02框架构图_底图.png b/src/assets/images/film/shooting/02框架构图_底图.png new file mode 100644 index 0000000..cd1b86a Binary files /dev/null and b/src/assets/images/film/shooting/02框架构图_底图.png differ diff --git a/src/assets/images/film/shooting/02框架构图_覆盖.png b/src/assets/images/film/shooting/02框架构图_覆盖.png new file mode 100644 index 0000000..40b880c Binary files /dev/null and b/src/assets/images/film/shooting/02框架构图_覆盖.png differ diff --git a/src/assets/images/film/shooting/03对称构图_底图.png b/src/assets/images/film/shooting/03对称构图_底图.png new file mode 100644 index 0000000..115d393 Binary files /dev/null and b/src/assets/images/film/shooting/03对称构图_底图.png differ diff --git a/src/assets/images/film/shooting/03对称构图_覆盖.png b/src/assets/images/film/shooting/03对称构图_覆盖.png new file mode 100644 index 0000000..d143d7f Binary files /dev/null and b/src/assets/images/film/shooting/03对称构图_覆盖.png differ diff --git a/src/assets/images/film/shooting/04对称构图_底图.png b/src/assets/images/film/shooting/04对称构图_底图.png new file mode 100644 index 0000000..e7c73fb Binary files /dev/null and b/src/assets/images/film/shooting/04对称构图_底图.png differ diff --git a/src/assets/images/film/shooting/04对称构图_覆盖.png b/src/assets/images/film/shooting/04对称构图_覆盖.png new file mode 100644 index 0000000..54910d9 Binary files /dev/null and b/src/assets/images/film/shooting/04对称构图_覆盖.png differ diff --git a/src/assets/images/film/shooting/05引导线构图_底图.png b/src/assets/images/film/shooting/05引导线构图_底图.png new file mode 100644 index 0000000..2238e14 Binary files /dev/null and b/src/assets/images/film/shooting/05引导线构图_底图.png differ diff --git a/src/assets/images/film/shooting/05引导线构图_覆盖.png b/src/assets/images/film/shooting/05引导线构图_覆盖.png new file mode 100644 index 0000000..0c907ea Binary files /dev/null and b/src/assets/images/film/shooting/05引导线构图_覆盖.png differ diff --git a/src/assets/images/film/shooting/06引导线构图_底图.png b/src/assets/images/film/shooting/06引导线构图_底图.png new file mode 100644 index 0000000..61bc64d Binary files /dev/null and b/src/assets/images/film/shooting/06引导线构图_底图.png differ diff --git a/src/assets/images/film/shooting/06引导线构图_覆盖.png b/src/assets/images/film/shooting/06引导线构图_覆盖.png new file mode 100644 index 0000000..aa58cc2 Binary files /dev/null and b/src/assets/images/film/shooting/06引导线构图_覆盖.png differ diff --git a/src/assets/images/film/shooting/对称构图.png b/src/assets/images/film/shooting/对称构图.png new file mode 100644 index 0000000..eb90317 Binary files /dev/null and b/src/assets/images/film/shooting/对称构图.png differ diff --git a/src/assets/images/film/shooting/引导线构图.png b/src/assets/images/film/shooting/引导线构图.png new file mode 100644 index 0000000..25ab2bd Binary files /dev/null and b/src/assets/images/film/shooting/引导线构图.png differ diff --git a/src/assets/images/film/shooting/徐汇滨江.png b/src/assets/images/film/shooting/徐汇滨江.png new file mode 100644 index 0000000..8c930ac Binary files /dev/null and b/src/assets/images/film/shooting/徐汇滨江.png differ diff --git a/src/assets/images/film/shooting/构图指南底图.png b/src/assets/images/film/shooting/构图指南底图.png new file mode 100644 index 0000000..faaf60a Binary files /dev/null and b/src/assets/images/film/shooting/构图指南底图.png differ diff --git a/src/assets/images/film/shooting/框架构图.png b/src/assets/images/film/shooting/框架构图.png new file mode 100644 index 0000000..f499fa3 Binary files /dev/null and b/src/assets/images/film/shooting/框架构图.png differ diff --git a/src/assets/images/film/shooting/油罐艺术公园入口.png b/src/assets/images/film/shooting/油罐艺术公园入口.png new file mode 100644 index 0000000..d3dfbb6 Binary files /dev/null and b/src/assets/images/film/shooting/油罐艺术公园入口.png differ diff --git a/src/assets/images/film/shooting/油罐艺术公园拍摄.png b/src/assets/images/film/shooting/油罐艺术公园拍摄.png new file mode 100644 index 0000000..757dddd Binary files /dev/null and b/src/assets/images/film/shooting/油罐艺术公园拍摄.png differ diff --git a/src/assets/images/film/shooting/油罐艺术公园模糊底图.png b/src/assets/images/film/shooting/油罐艺术公园模糊底图.png new file mode 100644 index 0000000..7da34b9 Binary files /dev/null and b/src/assets/images/film/shooting/油罐艺术公园模糊底图.png differ diff --git a/src/assets/images/film/shooting/油罐艺术公园清晰底图.png b/src/assets/images/film/shooting/油罐艺术公园清晰底图.png new file mode 100644 index 0000000..0f031cc Binary files /dev/null and b/src/assets/images/film/shooting/油罐艺术公园清晰底图.png differ diff --git a/src/assets/images/film/shooting/示意图-对称.png b/src/assets/images/film/shooting/示意图-对称.png new file mode 100644 index 0000000..2d726c6 Binary files /dev/null and b/src/assets/images/film/shooting/示意图-对称.png differ diff --git a/src/assets/images/film/shooting/示意图-引导.png b/src/assets/images/film/shooting/示意图-引导.png new file mode 100644 index 0000000..f718a25 Binary files /dev/null and b/src/assets/images/film/shooting/示意图-引导.png differ diff --git a/src/assets/images/film/shooting/示意图-框架.png b/src/assets/images/film/shooting/示意图-框架.png new file mode 100644 index 0000000..5f5a78d Binary files /dev/null and b/src/assets/images/film/shooting/示意图-框架.png differ diff --git a/src/assets/images/film/shooting/龙美术馆入口.png b/src/assets/images/film/shooting/龙美术馆入口.png new file mode 100644 index 0000000..6b6299e Binary files /dev/null and b/src/assets/images/film/shooting/龙美术馆入口.png differ diff --git a/src/assets/images/film/shooting/龙美术馆拍摄.png b/src/assets/images/film/shooting/龙美术馆拍摄.png new file mode 100644 index 0000000..51e2b0b Binary files /dev/null and b/src/assets/images/film/shooting/龙美术馆拍摄.png differ diff --git a/src/assets/images/film/shooting/龙美术馆模糊底图.png b/src/assets/images/film/shooting/龙美术馆模糊底图.png new file mode 100644 index 0000000..9dabe30 Binary files /dev/null and b/src/assets/images/film/shooting/龙美术馆模糊底图.png differ diff --git a/src/assets/images/film/shooting/龙美术馆清晰图.png b/src/assets/images/film/shooting/龙美术馆清晰图.png new file mode 100644 index 0000000..1ec4304 Binary files /dev/null and b/src/assets/images/film/shooting/龙美术馆清晰图.png differ diff --git a/src/assets/images/film/任务取景框背景.png b/src/assets/images/film/任务取景框背景.png new file mode 100644 index 0000000..26bd3d4 Binary files /dev/null and b/src/assets/images/film/任务取景框背景.png differ diff --git a/src/assets/images/film/分镜制作-拍摄分镜.png b/src/assets/images/film/分镜制作-拍摄分镜.png new file mode 100644 index 0000000..f204982 Binary files /dev/null and b/src/assets/images/film/分镜制作-拍摄分镜.png differ diff --git a/src/assets/images/film/创意构想-拍摄大纲.png b/src/assets/images/film/创意构想-拍摄大纲.png new file mode 100644 index 0000000..0b5d8b0 Binary files /dev/null and b/src/assets/images/film/创意构想-拍摄大纲.png differ diff --git a/src/assets/images/film/制作变焦镜-这里是徐汇滨江.png b/src/assets/images/film/制作变焦镜-这里是徐汇滨江.png new file mode 100644 index 0000000..7149dd8 Binary files /dev/null and b/src/assets/images/film/制作变焦镜-这里是徐汇滨江.png differ diff --git a/src/assets/images/film/思路提点全屏.png b/src/assets/images/film/思路提点全屏.png new file mode 100644 index 0000000..9d8c302 Binary files /dev/null and b/src/assets/images/film/思路提点全屏.png differ diff --git a/src/assets/images/film/思路提点小屏.png b/src/assets/images/film/思路提点小屏.png new file mode 100644 index 0000000..b1f307a Binary files /dev/null and b/src/assets/images/film/思路提点小屏.png differ diff --git a/src/assets/images/film/案件赏析小屏.png b/src/assets/images/film/案件赏析小屏.png new file mode 100644 index 0000000..e81026f Binary files /dev/null and b/src/assets/images/film/案件赏析小屏.png differ diff --git a/src/assets/images/film/电影-创作与表达.png b/src/assets/images/film/电影-创作与表达.png new file mode 100644 index 0000000..d7ec16f Binary files /dev/null and b/src/assets/images/film/电影-创作与表达.png differ diff --git a/src/assets/images/film/电影模块背景.png b/src/assets/images/film/电影模块背景.png new file mode 100644 index 0000000..cc16681 Binary files /dev/null and b/src/assets/images/film/电影模块背景.png differ diff --git a/src/assets/images/film/脚本编写-拍摄脚本.png b/src/assets/images/film/脚本编写-拍摄脚本.png new file mode 100644 index 0000000..7c492ae Binary files /dev/null and b/src/assets/images/film/脚本编写-拍摄脚本.png differ diff --git a/src/assets/images/home/action/光.png b/src/assets/images/home/action/光.png new file mode 100644 index 0000000..c1d31a5 Binary files /dev/null and b/src/assets/images/home/action/光.png differ diff --git a/src/assets/images/home/action/新手导航光.png b/src/assets/images/home/action/新手导航光.png new file mode 100644 index 0000000..e34c4c1 Binary files /dev/null and b/src/assets/images/home/action/新手导航光.png differ diff --git a/src/assets/images/home/action/新手导航构图.png b/src/assets/images/home/action/新手导航构图.png new file mode 100644 index 0000000..6ac4240 Binary files /dev/null and b/src/assets/images/home/action/新手导航构图.png differ diff --git a/src/assets/images/home/action/新手导航运镜.png b/src/assets/images/home/action/新手导航运镜.png new file mode 100644 index 0000000..91b4cce Binary files /dev/null and b/src/assets/images/home/action/新手导航运镜.png differ diff --git a/src/assets/images/home/action/构图.png b/src/assets/images/home/action/构图.png new file mode 100644 index 0000000..efbf778 Binary files /dev/null and b/src/assets/images/home/action/构图.png differ diff --git a/src/assets/images/home/action/运镜.png b/src/assets/images/home/action/运镜.png new file mode 100644 index 0000000..9a4edc5 Binary files /dev/null and b/src/assets/images/home/action/运镜.png differ diff --git a/src/assets/images/shooting/shoot.png b/src/assets/images/shooting/shoot.png new file mode 100644 index 0000000..ac5e81b Binary files /dev/null and b/src/assets/images/shooting/shoot.png differ diff --git a/src/assets/images/vlog/制作变焦镜/实地取景/取景大图.png b/src/assets/images/shooting/取景大图.png similarity index 100% rename from src/assets/images/vlog/制作变焦镜/实地取景/取景大图.png rename to src/assets/images/shooting/取景大图.png diff --git a/src/assets/images/vlog/制作变焦镜/实地取景/取景结果-样式外框.png b/src/assets/images/shooting/取景结果-样式外框.png similarity index 100% rename from src/assets/images/vlog/制作变焦镜/实地取景/取景结果-样式外框.png rename to src/assets/images/shooting/取景结果-样式外框.png diff --git a/src/assets/images/vlog/制作变焦镜/实地取景/拍摄装饰.png b/src/assets/images/shooting/拍摄装饰.png similarity index 100% rename from src/assets/images/vlog/制作变焦镜/实地取景/拍摄装饰.png rename to src/assets/images/shooting/拍摄装饰.png diff --git a/src/assets/images/vlog/制作变焦镜/实地取景/按钮-取景.png b/src/assets/images/shooting/按钮-取景.png similarity index 100% rename from src/assets/images/vlog/制作变焦镜/实地取景/按钮-取景.png rename to src/assets/images/shooting/按钮-取景.png diff --git a/src/assets/images/vlog/制作变焦镜/实地取景/按钮-拍摄.png b/src/assets/images/shooting/按钮-拍摄.png similarity index 100% rename from src/assets/images/vlog/制作变焦镜/实地取景/按钮-拍摄.png rename to src/assets/images/shooting/按钮-拍摄.png diff --git a/src/assets/images/afterditing/标题-取景地a.png b/src/assets/images/shooting/标题-取景地a.png similarity index 100% rename from src/assets/images/afterditing/标题-取景地a.png rename to src/assets/images/shooting/标题-取景地a.png diff --git a/src/assets/images/afterditing/标题-取景地b.png b/src/assets/images/shooting/标题-取景地b.png similarity index 100% rename from src/assets/images/afterditing/标题-取景地b.png rename to src/assets/images/shooting/标题-取景地b.png diff --git a/src/assets/images/vlog/practice/下滑.png b/src/assets/images/vlog/practice/下滑.png new file mode 100644 index 0000000..e730cba Binary files /dev/null and b/src/assets/images/vlog/practice/下滑.png differ diff --git a/src/assets/images/vlog/practice/他人实战作品.png b/src/assets/images/vlog/practice/他人实战作品.png new file mode 100644 index 0000000..b912571 Binary files /dev/null and b/src/assets/images/vlog/practice/他人实战作品.png differ diff --git a/src/assets/images/vlog/practice/作品底板.png b/src/assets/images/vlog/practice/作品底板.png new file mode 100644 index 0000000..91b9032 Binary files /dev/null and b/src/assets/images/vlog/practice/作品底板.png differ diff --git a/src/assets/images/vlog/practice/保存.png b/src/assets/images/vlog/practice/保存.png new file mode 100644 index 0000000..91e9f56 Binary files /dev/null and b/src/assets/images/vlog/practice/保存.png differ diff --git a/src/assets/images/vlog/practice/创作笔记底图.png b/src/assets/images/vlog/practice/创作笔记底图.png new file mode 100644 index 0000000..9537bd9 Binary files /dev/null and b/src/assets/images/vlog/practice/创作笔记底图.png differ diff --git a/src/assets/images/vlog/practice/图片遮罩.png b/src/assets/images/vlog/practice/图片遮罩.png new file mode 100644 index 0000000..bc7957a Binary files /dev/null and b/src/assets/images/vlog/practice/图片遮罩.png differ diff --git a/src/assets/images/vlog/practice/实战操作台效果图.png b/src/assets/images/vlog/practice/实战操作台效果图.png new file mode 100644 index 0000000..90313f8 Binary files /dev/null and b/src/assets/images/vlog/practice/实战操作台效果图.png differ diff --git a/src/assets/images/vlog/practice/实战操作台首页.png b/src/assets/images/vlog/practice/实战操作台首页.png new file mode 100644 index 0000000..ce91fb0 Binary files /dev/null and b/src/assets/images/vlog/practice/实战操作台首页.png differ diff --git a/src/assets/images/vlog/practice/我的创作笔记.png b/src/assets/images/vlog/practice/我的创作笔记.png new file mode 100644 index 0000000..9afb2fa Binary files /dev/null and b/src/assets/images/vlog/practice/我的创作笔记.png differ diff --git a/src/assets/images/vlog/practice/提交.png b/src/assets/images/vlog/practice/提交.png new file mode 100644 index 0000000..876d020 Binary files /dev/null and b/src/assets/images/vlog/practice/提交.png differ diff --git a/src/assets/images/vlog/practice/摄像机-人.png b/src/assets/images/vlog/practice/摄像机-人.png new file mode 100644 index 0000000..9b08c09 Binary files /dev/null and b/src/assets/images/vlog/practice/摄像机-人.png differ diff --git a/src/assets/images/vlog/practice/摄像机.png b/src/assets/images/vlog/practice/摄像机.png new file mode 100644 index 0000000..2398921 Binary files /dev/null and b/src/assets/images/vlog/practice/摄像机.png differ diff --git a/src/assets/images/vlog/practice/星星-亮.png b/src/assets/images/vlog/practice/星星-亮.png new file mode 100644 index 0000000..8e9d297 Binary files /dev/null and b/src/assets/images/vlog/practice/星星-亮.png differ diff --git a/src/assets/images/vlog/practice/星星-灰.png b/src/assets/images/vlog/practice/星星-灰.png new file mode 100644 index 0000000..b8288eb Binary files /dev/null and b/src/assets/images/vlog/practice/星星-灰.png differ diff --git a/src/assets/images/vlog/practice/查看.png b/src/assets/images/vlog/practice/查看.png new file mode 100644 index 0000000..a8a641a Binary files /dev/null and b/src/assets/images/vlog/practice/查看.png differ diff --git a/src/assets/images/vlog/practice/查看任务要求.png b/src/assets/images/vlog/practice/查看任务要求.png new file mode 100644 index 0000000..92e4e38 Binary files /dev/null and b/src/assets/images/vlog/practice/查看任务要求.png differ diff --git a/src/assets/images/vlog/practice/滑动任务面板.png b/src/assets/images/vlog/practice/滑动任务面板.png new file mode 100644 index 0000000..3c27f3b Binary files /dev/null and b/src/assets/images/vlog/practice/滑动任务面板.png differ diff --git a/src/assets/images/vlog/practice/滑动右.png b/src/assets/images/vlog/practice/滑动右.png new file mode 100644 index 0000000..fcc3a56 Binary files /dev/null and b/src/assets/images/vlog/practice/滑动右.png differ diff --git a/src/assets/images/vlog/practice/滑动左.png b/src/assets/images/vlog/practice/滑动左.png new file mode 100644 index 0000000..fcca4ea Binary files /dev/null and b/src/assets/images/vlog/practice/滑动左.png differ diff --git a/src/assets/images/vlog/practice/滑动表内文字.png b/src/assets/images/vlog/practice/滑动表内文字.png new file mode 100644 index 0000000..ce91eef Binary files /dev/null and b/src/assets/images/vlog/practice/滑动表内文字.png differ diff --git a/src/assets/images/vlog/practice/滑动遮罩.png b/src/assets/images/vlog/practice/滑动遮罩.png new file mode 100644 index 0000000..b17ce78 Binary files /dev/null and b/src/assets/images/vlog/practice/滑动遮罩.png differ diff --git a/src/assets/images/vlog/practice/背景1-人.png b/src/assets/images/vlog/practice/背景1-人.png new file mode 100644 index 0000000..bb4721c Binary files /dev/null and b/src/assets/images/vlog/practice/背景1-人.png differ diff --git a/src/assets/images/vlog/practice/背景1.png b/src/assets/images/vlog/practice/背景1.png new file mode 100644 index 0000000..31be801 Binary files /dev/null and b/src/assets/images/vlog/practice/背景1.png differ diff --git a/src/assets/images/vlog/practice/背景2.png b/src/assets/images/vlog/practice/背景2.png new file mode 100644 index 0000000..d08e819 Binary files /dev/null and b/src/assets/images/vlog/practice/背景2.png differ diff --git a/src/assets/images/vlog/practice/自评.png b/src/assets/images/vlog/practice/自评.png new file mode 100644 index 0000000..cef282c Binary files /dev/null and b/src/assets/images/vlog/practice/自评.png differ diff --git a/src/assets/images/vlog/practice/视频背景.png b/src/assets/images/vlog/practice/视频背景.png new file mode 100644 index 0000000..fc10873 Binary files /dev/null and b/src/assets/images/vlog/practice/视频背景.png differ diff --git a/src/assets/images/vlog/practice/评分.png b/src/assets/images/vlog/practice/评分.png new file mode 100644 index 0000000..4974dea Binary files /dev/null and b/src/assets/images/vlog/practice/评分.png differ diff --git a/src/assets/images/vlog/practice/评分底板.png b/src/assets/images/vlog/practice/评分底板.png new file mode 100644 index 0000000..46fa487 Binary files /dev/null and b/src/assets/images/vlog/practice/评分底板.png differ diff --git a/src/assets/images/vlog/practice/评分条.png b/src/assets/images/vlog/practice/评分条.png new file mode 100644 index 0000000..6721841 Binary files /dev/null and b/src/assets/images/vlog/practice/评分条.png differ diff --git a/src/assets/images/vlog/制作变焦镜/左边导航栏/标题-取景地a.png b/src/assets/images/vlog/制作变焦镜/左边导航栏/标题-取景地a.png deleted file mode 100644 index d8a2d0c..0000000 Binary files a/src/assets/images/vlog/制作变焦镜/左边导航栏/标题-取景地a.png and /dev/null differ diff --git a/src/assets/images/vlog/制作变焦镜/左边导航栏/标题-取景地b.png b/src/assets/images/vlog/制作变焦镜/左边导航栏/标题-取景地b.png deleted file mode 100644 index 762829b..0000000 Binary files a/src/assets/images/vlog/制作变焦镜/左边导航栏/标题-取景地b.png and /dev/null differ diff --git a/src/assets/images/work/未选中_带三角.png b/src/assets/images/work/未选中_带三角.png new file mode 100644 index 0000000..be10b2f Binary files /dev/null and b/src/assets/images/work/未选中_带三角.png differ diff --git a/src/assets/images/work/选中_带三角.png b/src/assets/images/work/选中_带三角.png new file mode 100644 index 0000000..d29cbd1 Binary files /dev/null and b/src/assets/images/work/选中_带三角.png differ diff --git a/src/assets/svg/back-to-top.svg b/src/assets/svg/back-to-top.svg new file mode 100644 index 0000000..1423dea --- /dev/null +++ b/src/assets/svg/back-to-top.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svg/deng.svg b/src/assets/svg/deng.svg new file mode 100644 index 0000000..bb7f301 --- /dev/null +++ b/src/assets/svg/deng.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Button/ImgButton.vue b/src/components/Button/ImgButton.vue index ec9043b..052ad8f 100644 --- a/src/components/Button/ImgButton.vue +++ b/src/components/Button/ImgButton.vue @@ -5,7 +5,12 @@ + + + + diff --git a/src/components/QuestionQuiz/index.ts b/src/components/QuestionQuiz/index.ts new file mode 100644 index 0000000..59de13a --- /dev/null +++ b/src/components/QuestionQuiz/index.ts @@ -0,0 +1,2 @@ +export { default as QuestionQuiz } from './QuestionQuiz.vue' +export type { Question, QuestionOption } from './QuestionQuiz.vue' diff --git a/src/components/Rating/index.ts b/src/components/Rating/index.ts new file mode 100644 index 0000000..69a23fe --- /dev/null +++ b/src/components/Rating/index.ts @@ -0,0 +1 @@ +export { default as StarRating } from './star-rating.vue' diff --git a/src/components/Rating/star-rating.vue b/src/components/Rating/star-rating.vue new file mode 100644 index 0000000..50fb116 --- /dev/null +++ b/src/components/Rating/star-rating.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/components/Sidebar/Sidebar.vue b/src/components/Sidebar/Sidebar.vue index b48ac0a..843b8f3 100644 --- a/src/components/Sidebar/Sidebar.vue +++ b/src/components/Sidebar/Sidebar.vue @@ -23,7 +23,7 @@ - - diff --git a/src/views/basic-elements/basic-shooting-skills/index.vue b/src/views/basic-elements/basic-shooting-skills/index.vue new file mode 100644 index 0000000..dc449d2 --- /dev/null +++ b/src/views/basic-elements/basic-shooting-skills/index.vue @@ -0,0 +1,303 @@ + + + + + diff --git a/src/views/basic-elements/camera-movement/index.vue b/src/views/basic-elements/camera-movement/index.vue new file mode 100644 index 0000000..bdbc8f8 --- /dev/null +++ b/src/views/basic-elements/camera-movement/index.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/src/views/basic-elements/composition/index.vue b/src/views/basic-elements/composition/index.vue new file mode 100644 index 0000000..ba3df1c --- /dev/null +++ b/src/views/basic-elements/composition/index.vue @@ -0,0 +1,150 @@ + + + + + diff --git a/src/views/basic-elements/light/index.vue b/src/views/basic-elements/light/index.vue new file mode 100644 index 0000000..c184ce8 --- /dev/null +++ b/src/views/basic-elements/light/index.vue @@ -0,0 +1,230 @@ + + + + + diff --git a/src/views/basic-elements/short-video-editing-skills/index.vue b/src/views/basic-elements/short-video-editing-skills/index.vue new file mode 100644 index 0000000..53a4037 --- /dev/null +++ b/src/views/basic-elements/short-video-editing-skills/index.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/src/views/film/after-editing/color-practice.vue b/src/views/film/after-editing/color-practice.vue new file mode 100644 index 0000000..2108d32 --- /dev/null +++ b/src/views/film/after-editing/color-practice.vue @@ -0,0 +1,49 @@ + + diff --git a/src/views/film/after-editing/color.vue b/src/views/film/after-editing/color.vue new file mode 100644 index 0000000..65ac15d --- /dev/null +++ b/src/views/film/after-editing/color.vue @@ -0,0 +1,312 @@ + + + + + diff --git a/src/views/film/after-editing/editing/association.vue b/src/views/film/after-editing/editing/association.vue new file mode 100644 index 0000000..d038675 --- /dev/null +++ b/src/views/film/after-editing/editing/association.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/views/film/after-editing/editing/continuity.vue b/src/views/film/after-editing/editing/continuity.vue new file mode 100644 index 0000000..2106a61 --- /dev/null +++ b/src/views/film/after-editing/editing/continuity.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/views/film/after-editing/editing/cut.vue b/src/views/film/after-editing/editing/cut.vue new file mode 100644 index 0000000..25703f0 --- /dev/null +++ b/src/views/film/after-editing/editing/cut.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/views/film/after-editing/editing/index.vue b/src/views/film/after-editing/editing/index.vue new file mode 100644 index 0000000..e84acae --- /dev/null +++ b/src/views/film/after-editing/editing/index.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/src/views/film/after-editing/editing/rhythm.vue b/src/views/film/after-editing/editing/rhythm.vue new file mode 100644 index 0000000..9833ec2 --- /dev/null +++ b/src/views/film/after-editing/editing/rhythm.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/views/film/after-editing/editing/sound-transition.vue b/src/views/film/after-editing/editing/sound-transition.vue new file mode 100644 index 0000000..0e8630e --- /dev/null +++ b/src/views/film/after-editing/editing/sound-transition.vue @@ -0,0 +1,141 @@ + + + diff --git a/src/views/film/after-editing/editing/start.vue b/src/views/film/after-editing/editing/start.vue new file mode 100644 index 0000000..49cf350 --- /dev/null +++ b/src/views/film/after-editing/editing/start.vue @@ -0,0 +1,59 @@ + + + diff --git a/src/views/film/after-editing/export-publish/index.vue b/src/views/film/after-editing/export-publish/index.vue new file mode 100644 index 0000000..a3891b8 --- /dev/null +++ b/src/views/film/after-editing/export-publish/index.vue @@ -0,0 +1,5 @@ + + + diff --git a/src/views/film/after-editing/sound-effect/index.vue b/src/views/film/after-editing/sound-effect/index.vue new file mode 100644 index 0000000..b8c15d5 --- /dev/null +++ b/src/views/film/after-editing/sound-effect/index.vue @@ -0,0 +1,63 @@ + + diff --git a/src/views/film/components/menu.vue b/src/views/film/components/menu.vue new file mode 100644 index 0000000..98d45b1 --- /dev/null +++ b/src/views/film/components/menu.vue @@ -0,0 +1,178 @@ + + + + + diff --git a/src/views/film/creative/index.vue b/src/views/film/creative/index.vue new file mode 100644 index 0000000..d87d8a7 --- /dev/null +++ b/src/views/film/creative/index.vue @@ -0,0 +1,49 @@ + + diff --git a/src/views/film/index.vue b/src/views/film/index.vue new file mode 100644 index 0000000..ac4f67a --- /dev/null +++ b/src/views/film/index.vue @@ -0,0 +1,116 @@ + + + diff --git a/src/views/film/midterm-shooting/equipment/index.vue b/src/views/film/midterm-shooting/equipment/index.vue new file mode 100644 index 0000000..d5d6a7e --- /dev/null +++ b/src/views/film/midterm-shooting/equipment/index.vue @@ -0,0 +1,520 @@ + + + diff --git a/src/views/midterm-shooting/midterm/index.vue b/src/views/film/midterm-shooting/midterm/index.vue similarity index 100% rename from src/views/midterm-shooting/midterm/index.vue rename to src/views/film/midterm-shooting/midterm/index.vue diff --git a/src/views/film/midterm-shooting/on-site/index.vue b/src/views/film/midterm-shooting/on-site/index.vue new file mode 100644 index 0000000..2a02021 --- /dev/null +++ b/src/views/film/midterm-shooting/on-site/index.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/views/film/midterm-shooting/on-site/low-angle.vue b/src/views/film/midterm-shooting/on-site/low-angle.vue new file mode 100644 index 0000000..8843e6b --- /dev/null +++ b/src/views/film/midterm-shooting/on-site/low-angle.vue @@ -0,0 +1,109 @@ + + + diff --git a/src/views/film/midterm-shooting/on-site/random.vue b/src/views/film/midterm-shooting/on-site/random.vue new file mode 100644 index 0000000..65d5647 --- /dev/null +++ b/src/views/film/midterm-shooting/on-site/random.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/views/film/midterm-shooting/on-site/slider.vue b/src/views/film/midterm-shooting/on-site/slider.vue new file mode 100644 index 0000000..6fe2231 --- /dev/null +++ b/src/views/film/midterm-shooting/on-site/slider.vue @@ -0,0 +1,93 @@ + + + diff --git a/src/views/film/midterm-shooting/on-site/slow-shutter-day.vue b/src/views/film/midterm-shooting/on-site/slow-shutter-day.vue new file mode 100644 index 0000000..d3e3cd0 --- /dev/null +++ b/src/views/film/midterm-shooting/on-site/slow-shutter-day.vue @@ -0,0 +1,110 @@ + + + diff --git a/src/views/film/midterm-shooting/on-site/slow-shutter-night.vue b/src/views/film/midterm-shooting/on-site/slow-shutter-night.vue new file mode 100644 index 0000000..6b5bc08 --- /dev/null +++ b/src/views/film/midterm-shooting/on-site/slow-shutter-night.vue @@ -0,0 +1,50 @@ + + + diff --git a/src/views/film/midterm-shooting/on-site/time-lapse.vue b/src/views/film/midterm-shooting/on-site/time-lapse.vue new file mode 100644 index 0000000..20179eb --- /dev/null +++ b/src/views/film/midterm-shooting/on-site/time-lapse.vue @@ -0,0 +1,119 @@ + + + diff --git a/src/views/film/midterm-shooting/shooting/components/StartOverlay.vue b/src/views/film/midterm-shooting/shooting/components/StartOverlay.vue new file mode 100644 index 0000000..d26c49f --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/components/StartOverlay.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/views/film/midterm-shooting/shooting/composables/useCanvasCapture.ts b/src/views/film/midterm-shooting/shooting/composables/useCanvasCapture.ts new file mode 100644 index 0000000..81c21bc --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/composables/useCanvasCapture.ts @@ -0,0 +1,77 @@ +import { ref } from 'vue' + +interface CaptureOptions { + width: number + height: number + scale?: number + delay?: number // 截图前的等待时间(用于 UI 更新) + resultDelay?: number // 截图后显示结果的延迟 + onDraw: (ctx: CanvasRenderingContext2D) => void | Promise + onComplete?: () => void +} + +export function useCanvasCapture() { + const isLoading = ref(false) + const showModal = ref(false) + const capturedImageUrl = ref('') + + const capture = async (options: CaptureOptions) => { + if (isLoading.value) return + + const { width, height, scale = 2, delay = 50, resultDelay = 0, onDraw, onComplete } = options + + isLoading.value = true + + try { + // 等待 UI 更新 + if (delay > 0) { + await new Promise(resolve => setTimeout(resolve, delay)) + } + + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + if (!ctx) { + throw new Error('Cannot get canvas context') + } + + // 设置画布大小(高清) + canvas.width = width * scale + canvas.height = height * scale + + // 填充白色背景(防止透明) + ctx.fillStyle = '#ffffff' + ctx.fillRect(0, 0, canvas.width, canvas.height) + + // 应用缩放 + ctx.scale(scale, scale) + + // 执行自定义绘制逻辑 + await onDraw(ctx) + + capturedImageUrl.value = canvas.toDataURL('image/png') + + // 延迟显示结果 + if (resultDelay > 0) { + setTimeout(() => { + isLoading.value = false + showModal.value = true + onComplete?.() + }, resultDelay) + } else { + isLoading.value = false + showModal.value = true + onComplete?.() + } + } catch (error) { + console.error('Capture failed:', error) + isLoading.value = false + } + } + + return { + isLoading, + showModal, + capturedImageUrl, + capture + } +} diff --git a/src/views/film/midterm-shooting/shooting/filmingLocation-a/index.vue b/src/views/film/midterm-shooting/shooting/filmingLocation-a/index.vue new file mode 100644 index 0000000..14106ca --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/filmingLocation-a/index.vue @@ -0,0 +1,330 @@ + + + + + diff --git a/src/views/film/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts b/src/views/film/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts new file mode 100644 index 0000000..b3d184e --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts @@ -0,0 +1,149 @@ +import { ref } from 'vue' +import { getTouchDistance, getClientPosition } from '../utils/touchGestures' + +/** + * 手机拍照模式的取景逻辑 + * 支持缩放和拖拽功能 + */ +export function useViewfinderA() { + // 缩放限制 + const MIN_ZOOM = 1 + const MAX_ZOOM = 3 + + // 缩放级别 (保留用于点击按钮) + const zoomLevels = [3, 2, 1] + const currentZoom = ref(1) + const isCapturing = ref(false) // 拍照闪光效果 + + // 拖拽相关状态 + const isDragging = ref(false) + const dragStartX = ref(0) + const dragStartY = ref(0) + const offsetX = ref(0) + const offsetY = ref(0) + const currentOffsetX = ref(0) + const currentOffsetY = ref(0) + + // 双指缩放相关 + const isPinching = ref(false) + const pinchStartDistance = ref(0) + const pinchStartZoom = ref(1) + + // 计算边界限制 + const limitOffset = () => { + const containerWidth = 871 + const containerHeight = 420 + const scale = currentZoom.value + + // 计算图片缩放后的尺寸 + const scaledWidth = containerWidth * scale + const scaledHeight = containerHeight * scale + + // 计算最大偏移量(图片边缘不能超出容器中心) + const maxOffsetX = (scaledWidth - containerWidth) / 2 + const maxOffsetY = (scaledHeight - containerHeight) / 2 + + // 限制偏移量 + currentOffsetX.value = Math.max(-maxOffsetX, Math.min(maxOffsetX, currentOffsetX.value)) + currentOffsetY.value = Math.max(-maxOffsetY, Math.min(maxOffsetY, currentOffsetY.value)) + } + + // 设置缩放级别 + const setZoom = (zoom: number) => { + currentZoom.value = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom)) + limitOffset() + } + + // 鼠标滚轮缩放 + const handleWheel = (e: WheelEvent) => { + e.preventDefault() + // 滚轮向下是正数(缩小),向上是负数(放大) + const delta = -e.deltaY * 0.001 + const newZoom = currentZoom.value + delta + setZoom(newZoom) + } + + // 开始拖拽 / 触摸 + const startDrag = (e: MouseEvent | TouchEvent) => { + // 双指缩放 + if ('touches' in e && e.touches.length === 2) { + isPinching.value = true + isDragging.value = false + pinchStartDistance.value = getTouchDistance(e.touches[0], e.touches[1]) + pinchStartZoom.value = currentZoom.value + e.preventDefault() + return + } + + // 单指拖拽 + if ('touches' in e && e.touches.length !== 1) return + + isDragging.value = true + isPinching.value = false + + const { x: clientX, y: clientY } = getClientPosition(e) + + dragStartX.value = clientX + dragStartY.value = clientY + offsetX.value = currentOffsetX.value + offsetY.value = currentOffsetY.value + } + + // 拖拽中 / 缩放中 + const onDrag = (e: MouseEvent | TouchEvent) => { + // 处理双指缩放 + if (isPinching.value && 'touches' in e && e.touches.length === 2) { + const currentDistance = getTouchDistance(e.touches[0], e.touches[1]) + const scale = currentDistance / pinchStartDistance.value + setZoom(pinchStartZoom.value * scale) + e.preventDefault() + return + } + + if (!isDragging.value) return + + const { x: clientX, y: clientY } = getClientPosition(e) + + const deltaX = clientX - dragStartX.value + const deltaY = clientY - dragStartY.value + + currentOffsetX.value = offsetX.value + deltaX + currentOffsetY.value = offsetY.value + deltaY + + limitOffset() + + // 防止滚动 + if (e.cancelable) { + e.preventDefault() + } + } + + // 结束拖拽 + const endDrag = () => { + isDragging.value = false + isPinching.value = false + } + + // 计算transform样式 + const getTransform = () => { + return `translate(${currentOffsetX.value}px, ${currentOffsetY.value}px) scale(${currentZoom.value})` + } + + return { + // 状态 + zoomLevels, + currentZoom, + isCapturing, + isDragging, + currentOffsetX, + currentOffsetY, + + // 方法 + setZoom, + startDrag, + onDrag, + endDrag, + handleWheel, + getTransform + } +} diff --git a/src/views/film/midterm-shooting/shooting/filmingLocation-b/index.vue b/src/views/film/midterm-shooting/shooting/filmingLocation-b/index.vue new file mode 100644 index 0000000..ff9bd96 --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/filmingLocation-b/index.vue @@ -0,0 +1,287 @@ + + + diff --git a/src/views/film/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts b/src/views/film/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts new file mode 100644 index 0000000..9e4d4cd --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts @@ -0,0 +1,291 @@ +import { ref, onMounted, onUnmounted } from 'vue' +import { getTouchDistance, getTouchCenter, getClientPosition } from '../utils/touchGestures' + +// 设计稿尺寸常量 +const DESIGN_WIDTH = 1920 +const DESIGN_HEIGHT = 1080 + +/** + * 自由取景框模式的逻辑 + * 支持拖动、四角调整大小、双指缩放 + */ +export function useViewfinderB() { + // 取景框相关 + const viewfinderContainer = ref() + const viewfinderBox = ref() + const viewfinder = ref({ + x: 660, // (1920 - 600) / 2 = 660,居中 + y: 340, // (1080 - 400) / 2 = 340,居中 + width: 600, + height: 400 + }) + + // 拖动相关状态 + const isDragging = ref(false) + const dragStart = ref({ x: 0, y: 0 }) + + // 调整大小相关状态 + const isResizing = ref(false) + const resizeCorner = ref<'nw' | 'ne' | 'sw' | 'se' | null>(null) + const resizeStart = ref({ + x: 0, + y: 0, + boxX: 0, + boxY: 0, + boxWidth: 0, + boxHeight: 0 + }) + + // 双指缩放相关状态 + const isPinching = ref(false) + const pinchStart = ref({ + distance: 0, + centerX: 0, + centerY: 0, + boxWidth: 0, + boxHeight: 0, + boxX: 0, + boxY: 0 + }) + + // 将屏幕坐标转换为缩放后的本地坐标 + const convertClientToLocal = (clientX: number, clientY: number) => { + if (!viewfinderContainer.value) return { x: clientX, y: clientY } + + const containerRect = viewfinderContainer.value.getBoundingClientRect() + + // 计算缩放比例 + const scaleX = containerRect.width / DESIGN_WIDTH + const scaleY = containerRect.height / DESIGN_HEIGHT + + // 计算相对于容器的坐标,并转换为设计稿坐标系 + const localX = (clientX - containerRect.left) / scaleX + const localY = (clientY - containerRect.top) / scaleY + + return { x: localX, y: localY } + } + + // 开始拖动取景框 + const startDrag = (e: MouseEvent | TouchEvent) => { + if (isResizing.value || isPinching.value) return + + // 检测双指触摸 + if ('touches' in e && e.touches.length === 2) { + // 双指缩放 + isPinching.value = true + const touch1 = e.touches[0] + const touch2 = e.touches[1] + + pinchStart.value = { + distance: getTouchDistance(touch1, touch2), + centerX: (touch1.clientX + touch2.clientX) / 2, + centerY: (touch1.clientY + touch2.clientY) / 2, + boxWidth: viewfinder.value.width, + boxHeight: viewfinder.value.height, + boxX: viewfinder.value.x, + boxY: viewfinder.value.y + } + e.preventDefault() + return + } + + // 单指拖动 + if ('touches' in e && e.touches.length !== 1) return + + isDragging.value = true + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + dragStart.value = { + x: localPos.x - viewfinder.value.x, + y: localPos.y - viewfinder.value.y + } + + e.preventDefault() + } + + // 开始调整大小 + const startResize = (corner: 'nw' | 'ne' | 'sw' | 'se', e: MouseEvent | TouchEvent) => { + isResizing.value = true + resizeCorner.value = corner + + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + resizeStart.value = { + x: localPos.x, + y: localPos.y, + boxX: viewfinder.value.x, + boxY: viewfinder.value.y, + boxWidth: viewfinder.value.width, + boxHeight: viewfinder.value.height + } + + e.preventDefault() + } + + // 处理移动 + const handleMove = (e: MouseEvent | TouchEvent) => { + if (!isDragging.value && !isResizing.value && !isPinching.value) return + + // 处理双指缩放 + if (isPinching.value && 'touches' in e && e.touches.length === 2) { + const touch1 = e.touches[0] + const touch2 = e.touches[1] + + const currentDistance = getTouchDistance(touch1, touch2) + const scale = currentDistance / pinchStart.value.distance + + // 计算新的尺寸 + let newWidth = pinchStart.value.boxWidth * scale + let newHeight = pinchStart.value.boxHeight * scale + + const minSize = 200 + + // 限制最小和最大尺寸 + newWidth = Math.max(minSize, Math.min(newWidth, DESIGN_WIDTH)) + newHeight = Math.max(minSize, Math.min(newHeight, DESIGN_HEIGHT)) + + // 保持中心点不变,计算新的位置 + const center = getTouchCenter(touch1, touch2) + const localCenter = convertClientToLocal(center.x, center.y) + + let newX = localCenter.x - newWidth / 2 + let newY = localCenter.y - newHeight / 2 + + // 边界检测 + newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - newWidth)) + newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - newHeight)) + + viewfinder.value.width = newWidth + viewfinder.value.height = newHeight + viewfinder.value.x = newX + viewfinder.value.y = newY + + e.preventDefault() + return + } + + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + if (isDragging.value && viewfinderContainer.value) { + // 拖动取景框 + let newX = localPos.x - dragStart.value.x + let newY = localPos.y - dragStart.value.y + + // 边界检测 - 使用设计稿尺寸 + newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - viewfinder.value.width)) + newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - viewfinder.value.height)) + + viewfinder.value.x = newX + viewfinder.value.y = newY + } else if (isResizing.value && viewfinderContainer.value) { + // 调整取景框大小 + const deltaX = localPos.x - resizeStart.value.x + const deltaY = localPos.y - resizeStart.value.y + + const minSize = 200 // 最小尺寸 + + if (resizeCorner.value === 'se') { + // 右下角 - 使用设计稿尺寸限制 + viewfinder.value.width = Math.max( + minSize, + Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) + ) + viewfinder.value.height = Math.max( + minSize, + Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) + ) + } else if (resizeCorner.value === 'sw') { + // 左下角 - 使用设计稿尺寸限制 + const newWidth = resizeStart.value.boxWidth - deltaX + const newX = resizeStart.value.boxX + deltaX + if (newWidth >= minSize && newX >= 0) { + viewfinder.value.x = newX + viewfinder.value.width = newWidth + } + viewfinder.value.height = Math.max( + minSize, + Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) + ) + } else if (resizeCorner.value === 'ne') { + // 右上角 - 使用设计稿尺寸限制 + const newHeight = resizeStart.value.boxHeight - deltaY + const newY = resizeStart.value.boxY + deltaY + if (newHeight >= minSize && newY >= 0) { + viewfinder.value.y = newY + viewfinder.value.height = newHeight + } + viewfinder.value.width = Math.max( + minSize, + Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) + ) + } else if (resizeCorner.value === 'nw') { + // 左上角 - 使用设计稿尺寸限制 + const newWidth = resizeStart.value.boxWidth - deltaX + const newHeight = resizeStart.value.boxHeight - deltaY + const newX = resizeStart.value.boxX + deltaX + const newY = resizeStart.value.boxY + deltaY + + if (newWidth >= minSize && newX >= 0) { + viewfinder.value.x = newX + viewfinder.value.width = newWidth + } + if (newHeight >= minSize && newY >= 0) { + viewfinder.value.y = newY + viewfinder.value.height = newHeight + } + } + } + + e.preventDefault() + } + + // 结束拖动或调整 + const handleEnd = () => { + isDragging.value = false + isResizing.value = false + isPinching.value = false + resizeCorner.value = null + } + + // 挂载时添加全局事件监听 + onMounted(() => { + document.addEventListener('mousemove', handleMove) + document.addEventListener('mouseup', handleEnd) + document.addEventListener('touchmove', handleMove) + document.addEventListener('touchend', handleEnd) + }) + + // 卸载时移除事件监听 + onUnmounted(() => { + document.removeEventListener('mousemove', handleMove) + document.removeEventListener('mouseup', handleEnd) + document.removeEventListener('touchmove', handleMove) + document.removeEventListener('touchend', handleEnd) + }) + + return { + // Refs + viewfinderContainer, + viewfinderBox, + + // 状态 + viewfinder, + isDragging, + isResizing, + isPinching, + + // 方法 + startDrag, + startResize, + + // 常量 + DESIGN_WIDTH, + DESIGN_HEIGHT + } +} diff --git a/src/views/film/midterm-shooting/shooting/index.vue b/src/views/film/midterm-shooting/shooting/index.vue new file mode 100644 index 0000000..ecb0e6c --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/index.vue @@ -0,0 +1,210 @@ + + + diff --git a/src/views/film/midterm-shooting/shooting/utils/touchGestures.ts b/src/views/film/midterm-shooting/shooting/utils/touchGestures.ts new file mode 100644 index 0000000..ce27a3a --- /dev/null +++ b/src/views/film/midterm-shooting/shooting/utils/touchGestures.ts @@ -0,0 +1,47 @@ +/** + * 触摸手势工具函数 + * 用于处理触摸事件的通用计算 + */ + +/** + * 计算两个触摸点之间的距离 + * @param touch1 第一个触摸点 + * @param touch2 第二个触摸点 + * @returns 两点之间的欧氏距离 + */ +export const getTouchDistance = (touch1: Touch, touch2: Touch): number => { + const dx = touch1.clientX - touch2.clientX + const dy = touch1.clientY - touch2.clientY + return Math.sqrt(dx * dx + dy * dy) +} + +/** + * 计算两个触摸点的中心坐标 + * @param touch1 第一个触摸点 + * @param touch2 第二个触摸点 + * @returns 中心点坐标 {x, y} + */ +export const getTouchCenter = (touch1: Touch, touch2: Touch) => { + return { + x: (touch1.clientX + touch2.clientX) / 2, + y: (touch1.clientY + touch2.clientY) / 2 + } +} + +/** + * 从鼠标或触摸事件中获取客户端坐标 + * @param e 鼠标事件或触摸事件 + * @returns 客户端坐标 {x, y} + */ +export const getClientPosition = (e: MouseEvent | TouchEvent) => { + if ('touches' in e && e.touches.length > 0) { + return { + x: e.touches[0].clientX, + y: e.touches[0].clientY + } + } + return { + x: (e as MouseEvent).clientX, + y: (e as MouseEvent).clientY + } +} diff --git a/src/views/film/practice/index.vue b/src/views/film/practice/index.vue new file mode 100644 index 0000000..f12dcfd --- /dev/null +++ b/src/views/film/practice/index.vue @@ -0,0 +1,349 @@ + + + + + diff --git a/src/views/film/practice/my-notes/index.vue b/src/views/film/practice/my-notes/index.vue new file mode 100644 index 0000000..9c84e47 --- /dev/null +++ b/src/views/film/practice/my-notes/index.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/src/views/film/practice/video/index.vue b/src/views/film/practice/video/index.vue new file mode 100644 index 0000000..4120c50 --- /dev/null +++ b/src/views/film/practice/video/index.vue @@ -0,0 +1,129 @@ + + + diff --git a/src/views/film/resource/index.vue b/src/views/film/resource/index.vue new file mode 100644 index 0000000..45b2962 --- /dev/null +++ b/src/views/film/resource/index.vue @@ -0,0 +1,31 @@ + + diff --git a/src/views/prepare/script-writing/doubao.vue b/src/views/film/script-writing/doubao.vue similarity index 100% rename from src/views/prepare/script-writing/doubao.vue rename to src/views/film/script-writing/doubao.vue diff --git a/src/views/film/script-writing/index.vue b/src/views/film/script-writing/index.vue new file mode 100644 index 0000000..410304c --- /dev/null +++ b/src/views/film/script-writing/index.vue @@ -0,0 +1,60 @@ + + diff --git a/src/views/film/script-writing/sample.vue b/src/views/film/script-writing/sample.vue new file mode 100644 index 0000000..8dd6635 --- /dev/null +++ b/src/views/film/script-writing/sample.vue @@ -0,0 +1,180 @@ + + + diff --git a/src/views/film/script-writing/writing.vue b/src/views/film/script-writing/writing.vue new file mode 100644 index 0000000..99524ee --- /dev/null +++ b/src/views/film/script-writing/writing.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/views/film/storyboard/index.vue b/src/views/film/storyboard/index.vue new file mode 100644 index 0000000..3123671 --- /dev/null +++ b/src/views/film/storyboard/index.vue @@ -0,0 +1,49 @@ + + diff --git a/src/views/film/video/index.vue b/src/views/film/video/index.vue new file mode 100644 index 0000000..67d7cd0 --- /dev/null +++ b/src/views/film/video/index.vue @@ -0,0 +1,316 @@ + + + + + diff --git a/src/views/film/work/components/WorkSideSwitcher.vue b/src/views/film/work/components/WorkSideSwitcher.vue new file mode 100644 index 0000000..6bc3bc8 --- /dev/null +++ b/src/views/film/work/components/WorkSideSwitcher.vue @@ -0,0 +1,66 @@ + + + diff --git a/src/views/film/work/index.vue b/src/views/film/work/index.vue new file mode 100644 index 0000000..96badd1 --- /dev/null +++ b/src/views/film/work/index.vue @@ -0,0 +1,194 @@ + + + diff --git a/src/views/guide/index.vue b/src/views/guide/index.vue deleted file mode 100644 index 80726e0..0000000 --- a/src/views/guide/index.vue +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/src/views/home/action.vue b/src/views/home/action.vue index fad8304..0f55b17 100644 --- a/src/views/home/action.vue +++ b/src/views/home/action.vue @@ -1,25 +1,29 @@ diff --git a/src/views/home/components/ModuleSwitcher/ModuleSwitcher.vue b/src/views/home/components/ModuleSwitcher/ModuleSwitcher.vue index 588046b..dce30c3 100644 --- a/src/views/home/components/ModuleSwitcher/ModuleSwitcher.vue +++ b/src/views/home/components/ModuleSwitcher/ModuleSwitcher.vue @@ -67,7 +67,7 @@ const moduleConfigs: ModuleConfig[] = [ } }, { - type: 'film', + type: 'interview', normalImage: IMAGE_PATHS.ACTION.MODULE_BTN2, selectedImage: IMAGE_PATHS.ACTION.SELECTED_BTN2, className: 'w-[1087px] h-[822px] left-[calc(50%+50px)] -translate-x-1/2 -bottom-4', @@ -86,7 +86,7 @@ const moduleConfigs: ModuleConfig[] = [ } }, { - type: 'interview', + type: 'film', normalImage: IMAGE_PATHS.ACTION.MODULE_BTN3, selectedImage: IMAGE_PATHS.ACTION.SELECTED_BTN3, className: 'w-[943px] h-[822px] right-[320px] -bottom-4', diff --git a/src/views/afterditing/adjustmentColor.vue b/src/views/interview/after-editing/adjustmentColor.vue similarity index 96% rename from src/views/afterditing/adjustmentColor.vue rename to src/views/interview/after-editing/adjustmentColor.vue index 23c364d..baa3ab4 100644 --- a/src/views/afterditing/adjustmentColor.vue +++ b/src/views/interview/after-editing/adjustmentColor.vue @@ -7,7 +7,7 @@ import { IMAGE_PATHS } from '@/utils' const router = useRouter() // 导入视频文件 -import videoadjustmentColor from '/video/Vlog模块_制作变焦镜_后期剪辑_调色.mp4' +import videoadjustmentColor from '/video/vlog_renwuqujing_tiaose.mp4' const videoList = [videoadjustmentColor] const videoRef = ref() diff --git a/src/views/interview/after-editing/color.vue b/src/views/interview/after-editing/color.vue new file mode 100644 index 0000000..b4a15d8 --- /dev/null +++ b/src/views/interview/after-editing/color.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/src/views/afterditing/editing.vue b/src/views/interview/after-editing/editing.vue similarity index 84% rename from src/views/afterditing/editing.vue rename to src/views/interview/after-editing/editing.vue index 5c8a7fa..266edb4 100644 --- a/src/views/afterditing/editing.vue +++ b/src/views/interview/after-editing/editing.vue @@ -7,10 +7,10 @@ import { IMAGE_PATHS } from '@/utils' const router = useRouter() // 导入视频文件 -import videoBasic from '/video/Vlog模块_制作变焦镜_后期剪辑_基础剪辑操作.mp4' -import videoSoundeffect from '/video/Vlog模块_制作变焦镜_后期剪辑_音效处理.mp4' -import videoPackaging from '/video/Vlog模块_制作变焦镜_后期剪辑_包装(AI 字幕生成与样式调整).mp4' -import videoCopyright from '/video/Vlog模块_制作变焦镜_后期剪辑_版权伦理规范.mp4' +import videoBasic from '/video/vlog_zhizuobianjiaojing_jichujianji.mp4' +import videoSoundeffect from '/video/vlog_renwuqujing_yingxiaochuli.mp4' +import videoPackaging from '/video/vlog_renwuqujing_baozhuang.mp4' +import videoCopyright from '/video/vlog_renwuqujing_banquanguifan.mp4' const videoList = [videoBasic, videoSoundeffect, videoPackaging, videoCopyright] const videoRef = ref() diff --git a/src/views/interview/components/menu.vue b/src/views/interview/components/menu.vue new file mode 100644 index 0000000..764680d --- /dev/null +++ b/src/views/interview/components/menu.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/src/views/interview/index.vue b/src/views/interview/index.vue new file mode 100644 index 0000000..01a8ea4 --- /dev/null +++ b/src/views/interview/index.vue @@ -0,0 +1,111 @@ + + + diff --git a/src/views/midterm-shooting/equipment/index.vue b/src/views/interview/midterm-shooting/equipment/index.vue similarity index 90% rename from src/views/midterm-shooting/equipment/index.vue rename to src/views/interview/midterm-shooting/equipment/index.vue index 4ef0c5d..b6d02f7 100644 --- a/src/views/midterm-shooting/equipment/index.vue +++ b/src/views/interview/midterm-shooting/equipment/index.vue @@ -1,3 +1,89 @@ + - - diff --git a/src/views/interview/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts b/src/views/interview/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts new file mode 100644 index 0000000..b3d184e --- /dev/null +++ b/src/views/interview/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts @@ -0,0 +1,149 @@ +import { ref } from 'vue' +import { getTouchDistance, getClientPosition } from '../utils/touchGestures' + +/** + * 手机拍照模式的取景逻辑 + * 支持缩放和拖拽功能 + */ +export function useViewfinderA() { + // 缩放限制 + const MIN_ZOOM = 1 + const MAX_ZOOM = 3 + + // 缩放级别 (保留用于点击按钮) + const zoomLevels = [3, 2, 1] + const currentZoom = ref(1) + const isCapturing = ref(false) // 拍照闪光效果 + + // 拖拽相关状态 + const isDragging = ref(false) + const dragStartX = ref(0) + const dragStartY = ref(0) + const offsetX = ref(0) + const offsetY = ref(0) + const currentOffsetX = ref(0) + const currentOffsetY = ref(0) + + // 双指缩放相关 + const isPinching = ref(false) + const pinchStartDistance = ref(0) + const pinchStartZoom = ref(1) + + // 计算边界限制 + const limitOffset = () => { + const containerWidth = 871 + const containerHeight = 420 + const scale = currentZoom.value + + // 计算图片缩放后的尺寸 + const scaledWidth = containerWidth * scale + const scaledHeight = containerHeight * scale + + // 计算最大偏移量(图片边缘不能超出容器中心) + const maxOffsetX = (scaledWidth - containerWidth) / 2 + const maxOffsetY = (scaledHeight - containerHeight) / 2 + + // 限制偏移量 + currentOffsetX.value = Math.max(-maxOffsetX, Math.min(maxOffsetX, currentOffsetX.value)) + currentOffsetY.value = Math.max(-maxOffsetY, Math.min(maxOffsetY, currentOffsetY.value)) + } + + // 设置缩放级别 + const setZoom = (zoom: number) => { + currentZoom.value = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom)) + limitOffset() + } + + // 鼠标滚轮缩放 + const handleWheel = (e: WheelEvent) => { + e.preventDefault() + // 滚轮向下是正数(缩小),向上是负数(放大) + const delta = -e.deltaY * 0.001 + const newZoom = currentZoom.value + delta + setZoom(newZoom) + } + + // 开始拖拽 / 触摸 + const startDrag = (e: MouseEvent | TouchEvent) => { + // 双指缩放 + if ('touches' in e && e.touches.length === 2) { + isPinching.value = true + isDragging.value = false + pinchStartDistance.value = getTouchDistance(e.touches[0], e.touches[1]) + pinchStartZoom.value = currentZoom.value + e.preventDefault() + return + } + + // 单指拖拽 + if ('touches' in e && e.touches.length !== 1) return + + isDragging.value = true + isPinching.value = false + + const { x: clientX, y: clientY } = getClientPosition(e) + + dragStartX.value = clientX + dragStartY.value = clientY + offsetX.value = currentOffsetX.value + offsetY.value = currentOffsetY.value + } + + // 拖拽中 / 缩放中 + const onDrag = (e: MouseEvent | TouchEvent) => { + // 处理双指缩放 + if (isPinching.value && 'touches' in e && e.touches.length === 2) { + const currentDistance = getTouchDistance(e.touches[0], e.touches[1]) + const scale = currentDistance / pinchStartDistance.value + setZoom(pinchStartZoom.value * scale) + e.preventDefault() + return + } + + if (!isDragging.value) return + + const { x: clientX, y: clientY } = getClientPosition(e) + + const deltaX = clientX - dragStartX.value + const deltaY = clientY - dragStartY.value + + currentOffsetX.value = offsetX.value + deltaX + currentOffsetY.value = offsetY.value + deltaY + + limitOffset() + + // 防止滚动 + if (e.cancelable) { + e.preventDefault() + } + } + + // 结束拖拽 + const endDrag = () => { + isDragging.value = false + isPinching.value = false + } + + // 计算transform样式 + const getTransform = () => { + return `translate(${currentOffsetX.value}px, ${currentOffsetY.value}px) scale(${currentZoom.value})` + } + + return { + // 状态 + zoomLevels, + currentZoom, + isCapturing, + isDragging, + currentOffsetX, + currentOffsetY, + + // 方法 + setZoom, + startDrag, + onDrag, + endDrag, + handleWheel, + getTransform + } +} diff --git a/src/views/interview/midterm-shooting/shooting/filmingLocation-b/index.vue b/src/views/interview/midterm-shooting/shooting/filmingLocation-b/index.vue new file mode 100644 index 0000000..3235dec --- /dev/null +++ b/src/views/interview/midterm-shooting/shooting/filmingLocation-b/index.vue @@ -0,0 +1,258 @@ + + + diff --git a/src/views/interview/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts b/src/views/interview/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts new file mode 100644 index 0000000..9e4d4cd --- /dev/null +++ b/src/views/interview/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts @@ -0,0 +1,291 @@ +import { ref, onMounted, onUnmounted } from 'vue' +import { getTouchDistance, getTouchCenter, getClientPosition } from '../utils/touchGestures' + +// 设计稿尺寸常量 +const DESIGN_WIDTH = 1920 +const DESIGN_HEIGHT = 1080 + +/** + * 自由取景框模式的逻辑 + * 支持拖动、四角调整大小、双指缩放 + */ +export function useViewfinderB() { + // 取景框相关 + const viewfinderContainer = ref() + const viewfinderBox = ref() + const viewfinder = ref({ + x: 660, // (1920 - 600) / 2 = 660,居中 + y: 340, // (1080 - 400) / 2 = 340,居中 + width: 600, + height: 400 + }) + + // 拖动相关状态 + const isDragging = ref(false) + const dragStart = ref({ x: 0, y: 0 }) + + // 调整大小相关状态 + const isResizing = ref(false) + const resizeCorner = ref<'nw' | 'ne' | 'sw' | 'se' | null>(null) + const resizeStart = ref({ + x: 0, + y: 0, + boxX: 0, + boxY: 0, + boxWidth: 0, + boxHeight: 0 + }) + + // 双指缩放相关状态 + const isPinching = ref(false) + const pinchStart = ref({ + distance: 0, + centerX: 0, + centerY: 0, + boxWidth: 0, + boxHeight: 0, + boxX: 0, + boxY: 0 + }) + + // 将屏幕坐标转换为缩放后的本地坐标 + const convertClientToLocal = (clientX: number, clientY: number) => { + if (!viewfinderContainer.value) return { x: clientX, y: clientY } + + const containerRect = viewfinderContainer.value.getBoundingClientRect() + + // 计算缩放比例 + const scaleX = containerRect.width / DESIGN_WIDTH + const scaleY = containerRect.height / DESIGN_HEIGHT + + // 计算相对于容器的坐标,并转换为设计稿坐标系 + const localX = (clientX - containerRect.left) / scaleX + const localY = (clientY - containerRect.top) / scaleY + + return { x: localX, y: localY } + } + + // 开始拖动取景框 + const startDrag = (e: MouseEvent | TouchEvent) => { + if (isResizing.value || isPinching.value) return + + // 检测双指触摸 + if ('touches' in e && e.touches.length === 2) { + // 双指缩放 + isPinching.value = true + const touch1 = e.touches[0] + const touch2 = e.touches[1] + + pinchStart.value = { + distance: getTouchDistance(touch1, touch2), + centerX: (touch1.clientX + touch2.clientX) / 2, + centerY: (touch1.clientY + touch2.clientY) / 2, + boxWidth: viewfinder.value.width, + boxHeight: viewfinder.value.height, + boxX: viewfinder.value.x, + boxY: viewfinder.value.y + } + e.preventDefault() + return + } + + // 单指拖动 + if ('touches' in e && e.touches.length !== 1) return + + isDragging.value = true + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + dragStart.value = { + x: localPos.x - viewfinder.value.x, + y: localPos.y - viewfinder.value.y + } + + e.preventDefault() + } + + // 开始调整大小 + const startResize = (corner: 'nw' | 'ne' | 'sw' | 'se', e: MouseEvent | TouchEvent) => { + isResizing.value = true + resizeCorner.value = corner + + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + resizeStart.value = { + x: localPos.x, + y: localPos.y, + boxX: viewfinder.value.x, + boxY: viewfinder.value.y, + boxWidth: viewfinder.value.width, + boxHeight: viewfinder.value.height + } + + e.preventDefault() + } + + // 处理移动 + const handleMove = (e: MouseEvent | TouchEvent) => { + if (!isDragging.value && !isResizing.value && !isPinching.value) return + + // 处理双指缩放 + if (isPinching.value && 'touches' in e && e.touches.length === 2) { + const touch1 = e.touches[0] + const touch2 = e.touches[1] + + const currentDistance = getTouchDistance(touch1, touch2) + const scale = currentDistance / pinchStart.value.distance + + // 计算新的尺寸 + let newWidth = pinchStart.value.boxWidth * scale + let newHeight = pinchStart.value.boxHeight * scale + + const minSize = 200 + + // 限制最小和最大尺寸 + newWidth = Math.max(minSize, Math.min(newWidth, DESIGN_WIDTH)) + newHeight = Math.max(minSize, Math.min(newHeight, DESIGN_HEIGHT)) + + // 保持中心点不变,计算新的位置 + const center = getTouchCenter(touch1, touch2) + const localCenter = convertClientToLocal(center.x, center.y) + + let newX = localCenter.x - newWidth / 2 + let newY = localCenter.y - newHeight / 2 + + // 边界检测 + newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - newWidth)) + newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - newHeight)) + + viewfinder.value.width = newWidth + viewfinder.value.height = newHeight + viewfinder.value.x = newX + viewfinder.value.y = newY + + e.preventDefault() + return + } + + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + if (isDragging.value && viewfinderContainer.value) { + // 拖动取景框 + let newX = localPos.x - dragStart.value.x + let newY = localPos.y - dragStart.value.y + + // 边界检测 - 使用设计稿尺寸 + newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - viewfinder.value.width)) + newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - viewfinder.value.height)) + + viewfinder.value.x = newX + viewfinder.value.y = newY + } else if (isResizing.value && viewfinderContainer.value) { + // 调整取景框大小 + const deltaX = localPos.x - resizeStart.value.x + const deltaY = localPos.y - resizeStart.value.y + + const minSize = 200 // 最小尺寸 + + if (resizeCorner.value === 'se') { + // 右下角 - 使用设计稿尺寸限制 + viewfinder.value.width = Math.max( + minSize, + Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) + ) + viewfinder.value.height = Math.max( + minSize, + Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) + ) + } else if (resizeCorner.value === 'sw') { + // 左下角 - 使用设计稿尺寸限制 + const newWidth = resizeStart.value.boxWidth - deltaX + const newX = resizeStart.value.boxX + deltaX + if (newWidth >= minSize && newX >= 0) { + viewfinder.value.x = newX + viewfinder.value.width = newWidth + } + viewfinder.value.height = Math.max( + minSize, + Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) + ) + } else if (resizeCorner.value === 'ne') { + // 右上角 - 使用设计稿尺寸限制 + const newHeight = resizeStart.value.boxHeight - deltaY + const newY = resizeStart.value.boxY + deltaY + if (newHeight >= minSize && newY >= 0) { + viewfinder.value.y = newY + viewfinder.value.height = newHeight + } + viewfinder.value.width = Math.max( + minSize, + Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) + ) + } else if (resizeCorner.value === 'nw') { + // 左上角 - 使用设计稿尺寸限制 + const newWidth = resizeStart.value.boxWidth - deltaX + const newHeight = resizeStart.value.boxHeight - deltaY + const newX = resizeStart.value.boxX + deltaX + const newY = resizeStart.value.boxY + deltaY + + if (newWidth >= minSize && newX >= 0) { + viewfinder.value.x = newX + viewfinder.value.width = newWidth + } + if (newHeight >= minSize && newY >= 0) { + viewfinder.value.y = newY + viewfinder.value.height = newHeight + } + } + } + + e.preventDefault() + } + + // 结束拖动或调整 + const handleEnd = () => { + isDragging.value = false + isResizing.value = false + isPinching.value = false + resizeCorner.value = null + } + + // 挂载时添加全局事件监听 + onMounted(() => { + document.addEventListener('mousemove', handleMove) + document.addEventListener('mouseup', handleEnd) + document.addEventListener('touchmove', handleMove) + document.addEventListener('touchend', handleEnd) + }) + + // 卸载时移除事件监听 + onUnmounted(() => { + document.removeEventListener('mousemove', handleMove) + document.removeEventListener('mouseup', handleEnd) + document.removeEventListener('touchmove', handleMove) + document.removeEventListener('touchend', handleEnd) + }) + + return { + // Refs + viewfinderContainer, + viewfinderBox, + + // 状态 + viewfinder, + isDragging, + isResizing, + isPinching, + + // 方法 + startDrag, + startResize, + + // 常量 + DESIGN_WIDTH, + DESIGN_HEIGHT + } +} diff --git a/src/views/midterm-shooting/shooting/index.vue b/src/views/interview/midterm-shooting/shooting/index.vue similarity index 90% rename from src/views/midterm-shooting/shooting/index.vue rename to src/views/interview/midterm-shooting/shooting/index.vue index 4886fc3..ab8085f 100644 --- a/src/views/midterm-shooting/shooting/index.vue +++ b/src/views/interview/midterm-shooting/shooting/index.vue @@ -6,7 +6,7 @@ import { Overlay } from '@/components/Overlay' import { ImgButton } from '@/components/Button' import { getBackgroundImage, IMAGE_PATHS } from '@/utils' import shootingtitle from '@/assets/images/shooting/标题-实地取景.png' -import videoSrc from '/video/新手导航_九种构图法.mp4' +import videoSrc from '/video/xinshou_jiuzhonggoutufa.mp4' import KnowledgeCard from '@/components/KnowledgeCard/KnowledgeCard.vue' type Btn = { @@ -71,11 +71,11 @@ const currentCompositionKey = computed(() => { const handleClickA = () => { // 跳转设备准备 - router.push({ name: 'filmingLocation-a' }) + router.push({ name: 'InterviewFilmingLocationA' }) } const handleClickB = () => { // 跳转设备准备 - router.push({ name: 'filmingLocation-b' }) + router.push({ name: 'InterviewFilmingLocationB' }) } const back = () => { // 跳转设备准备 @@ -93,21 +93,16 @@ const back = () => {
- +
- +
- +
- +
@@ -116,7 +111,7 @@ const back = () => { :highlight-selector="'.mybutton'" :highlight-padding="12" :highlight-radius="20" - position-offset-y="70%" + position-offset-y="85%" > 在走进拍摄场景之前,我们先来查看一下构图指南。 diff --git a/src/views/interview/midterm-shooting/shooting/utils/touchGestures.ts b/src/views/interview/midterm-shooting/shooting/utils/touchGestures.ts new file mode 100644 index 0000000..ce27a3a --- /dev/null +++ b/src/views/interview/midterm-shooting/shooting/utils/touchGestures.ts @@ -0,0 +1,47 @@ +/** + * 触摸手势工具函数 + * 用于处理触摸事件的通用计算 + */ + +/** + * 计算两个触摸点之间的距离 + * @param touch1 第一个触摸点 + * @param touch2 第二个触摸点 + * @returns 两点之间的欧氏距离 + */ +export const getTouchDistance = (touch1: Touch, touch2: Touch): number => { + const dx = touch1.clientX - touch2.clientX + const dy = touch1.clientY - touch2.clientY + return Math.sqrt(dx * dx + dy * dy) +} + +/** + * 计算两个触摸点的中心坐标 + * @param touch1 第一个触摸点 + * @param touch2 第二个触摸点 + * @returns 中心点坐标 {x, y} + */ +export const getTouchCenter = (touch1: Touch, touch2: Touch) => { + return { + x: (touch1.clientX + touch2.clientX) / 2, + y: (touch1.clientY + touch2.clientY) / 2 + } +} + +/** + * 从鼠标或触摸事件中获取客户端坐标 + * @param e 鼠标事件或触摸事件 + * @returns 客户端坐标 {x, y} + */ +export const getClientPosition = (e: MouseEvent | TouchEvent) => { + if ('touches' in e && e.touches.length > 0) { + return { + x: e.touches[0].clientX, + y: e.touches[0].clientY + } + } + return { + x: (e as MouseEvent).clientX, + y: (e as MouseEvent).clientY + } +} diff --git a/src/views/interview/practice/index.vue b/src/views/interview/practice/index.vue new file mode 100644 index 0000000..091690b --- /dev/null +++ b/src/views/interview/practice/index.vue @@ -0,0 +1,357 @@ + + + + + diff --git a/src/views/interview/practice/my-notes/index.vue b/src/views/interview/practice/my-notes/index.vue new file mode 100644 index 0000000..290aa8b --- /dev/null +++ b/src/views/interview/practice/my-notes/index.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/src/views/interview/practice/video/index.vue b/src/views/interview/practice/video/index.vue new file mode 100644 index 0000000..4120c50 --- /dev/null +++ b/src/views/interview/practice/video/index.vue @@ -0,0 +1,129 @@ + + + diff --git a/src/views/interview/script-writing/doubao.vue b/src/views/interview/script-writing/doubao.vue new file mode 100644 index 0000000..e69de29 diff --git a/src/views/interview/script-writing/index.vue b/src/views/interview/script-writing/index.vue new file mode 100644 index 0000000..29851f1 --- /dev/null +++ b/src/views/interview/script-writing/index.vue @@ -0,0 +1,88 @@ + + diff --git a/src/views/interview/script-writing/sample.vue b/src/views/interview/script-writing/sample.vue new file mode 100644 index 0000000..10d41e2 --- /dev/null +++ b/src/views/interview/script-writing/sample.vue @@ -0,0 +1,180 @@ + + + diff --git a/src/views/interview/script-writing/writing.vue b/src/views/interview/script-writing/writing.vue new file mode 100644 index 0000000..99524ee --- /dev/null +++ b/src/views/interview/script-writing/writing.vue @@ -0,0 +1,61 @@ + + + diff --git a/src/views/interview/video/index.vue b/src/views/interview/video/index.vue new file mode 100644 index 0000000..5ec7250 --- /dev/null +++ b/src/views/interview/video/index.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/src/views/interview/work/components/WorkSideSwitcher.vue b/src/views/interview/work/components/WorkSideSwitcher.vue new file mode 100644 index 0000000..c8e38ff --- /dev/null +++ b/src/views/interview/work/components/WorkSideSwitcher.vue @@ -0,0 +1,66 @@ + + + diff --git a/src/views/interview/work/index.vue b/src/views/interview/work/index.vue new file mode 100644 index 0000000..0d677de --- /dev/null +++ b/src/views/interview/work/index.vue @@ -0,0 +1,197 @@ + + + diff --git a/src/views/midterm-shooting/shooting/filmingLocation-a/a-1.vue b/src/views/midterm-shooting/shooting/filmingLocation-a/a-1.vue deleted file mode 100644 index ecc8c5b..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-a/a-1.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/src/views/midterm-shooting/shooting/filmingLocation-a/a-2.vue b/src/views/midterm-shooting/shooting/filmingLocation-a/a-2.vue deleted file mode 100644 index 39bfff4..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-a/a-2.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/src/views/midterm-shooting/shooting/filmingLocation-a/a-3.vue b/src/views/midterm-shooting/shooting/filmingLocation-a/a-3.vue deleted file mode 100644 index a30e325..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-a/a-3.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/src/views/midterm-shooting/shooting/filmingLocation-a/index.vue b/src/views/midterm-shooting/shooting/filmingLocation-a/index.vue deleted file mode 100644 index 5eb3931..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-a/index.vue +++ /dev/null @@ -1,65 +0,0 @@ - - - diff --git a/src/views/midterm-shooting/shooting/filmingLocation-b/b-1.vue b/src/views/midterm-shooting/shooting/filmingLocation-b/b-1.vue deleted file mode 100644 index 8ce1978..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-b/b-1.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/src/views/midterm-shooting/shooting/filmingLocation-b/b-2.vue b/src/views/midterm-shooting/shooting/filmingLocation-b/b-2.vue deleted file mode 100644 index 6269c71..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-b/b-2.vue +++ /dev/null @@ -1,30 +0,0 @@ - - - diff --git a/src/views/midterm-shooting/shooting/filmingLocation-b/b-3.vue b/src/views/midterm-shooting/shooting/filmingLocation-b/b-3.vue deleted file mode 100644 index 2f6e15d..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-b/b-3.vue +++ /dev/null @@ -1,34 +0,0 @@ - - - diff --git a/src/views/midterm-shooting/shooting/filmingLocation-b/index.vue b/src/views/midterm-shooting/shooting/filmingLocation-b/index.vue deleted file mode 100644 index 5f389ac..0000000 --- a/src/views/midterm-shooting/shooting/filmingLocation-b/index.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - diff --git a/src/views/not-found.vue b/src/views/not-found.vue new file mode 100644 index 0000000..faceeca --- /dev/null +++ b/src/views/not-found.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/views/prepare/index.vue b/src/views/prepare/index.vue deleted file mode 100644 index 48592b8..0000000 --- a/src/views/prepare/index.vue +++ /dev/null @@ -1,190 +0,0 @@ - - - - diff --git a/src/views/prepare/script-writing/index.vue b/src/views/prepare/script-writing/index.vue deleted file mode 100644 index 1af4d35..0000000 --- a/src/views/prepare/script-writing/index.vue +++ /dev/null @@ -1,127 +0,0 @@ - - - diff --git a/src/views/preparedetail/index.vue b/src/views/preparedetail/index.vue deleted file mode 100644 index 724673d..0000000 --- a/src/views/preparedetail/index.vue +++ /dev/null @@ -1,49 +0,0 @@ - - - - - diff --git a/src/views/skill/components/CategoryTags.vue b/src/views/skill/components/CategoryTags.vue index ecbcad4..955e256 100644 --- a/src/views/skill/components/CategoryTags.vue +++ b/src/views/skill/components/CategoryTags.vue @@ -1,6 +1,4 @@ + + + + diff --git a/src/views/vlog/after-editing/color.vue b/src/views/vlog/after-editing/color.vue new file mode 100644 index 0000000..224bcf5 --- /dev/null +++ b/src/views/vlog/after-editing/color.vue @@ -0,0 +1,304 @@ + + + + + diff --git a/src/views/vlog/after-editing/editing.vue b/src/views/vlog/after-editing/editing.vue new file mode 100644 index 0000000..266edb4 --- /dev/null +++ b/src/views/vlog/after-editing/editing.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/src/views/vlog/components/menu.vue b/src/views/vlog/components/menu.vue new file mode 100644 index 0000000..9c0ed9d --- /dev/null +++ b/src/views/vlog/components/menu.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/src/views/vlog/index.vue b/src/views/vlog/index.vue index e0a200f..7662926 100644 --- a/src/views/vlog/index.vue +++ b/src/views/vlog/index.vue @@ -2,38 +2,52 @@ import { getBackgroundImage, IMAGE_PATHS } from '@/utils' import { ImgButton } from '@/components/Button' -import { onMounted, ref } from 'vue' -import { ProgressBar } from '@/components' -import { useRouter, useRoute } from 'vue-router' +import { computed, onMounted, ref, watch } from 'vue' +import { ProgressBar, Overlay } from '@/components' +import { useRouter } from 'vue-router' +import { useVlogStore } from '@/stores' +import VlogMenu from './components/menu.vue' const router = useRouter() -const route = useRoute() +const vlog = useVlogStore() +const visible = ref(false) -const currentStep = ref(0) +const currentStep = computed(() => vlog.vlogUnlockProgress) const handleComplete = () => { - console.log('完成前置条件') + // console.log('完成前置条件') // 处理完成逻辑 + router.push({ name: 'VlogPractice' }) } const handleStepChange = (step: number) => { - console.log('步骤变化:', step) // 处理步骤变化逻辑 + vlog.setVlogUnlockProgress(step) + visible.value = true } const handleWorkClick = () => { console.log('任务取景按钮点击') // 处理任务取景按钮点击逻辑 - router.push({ name: 'Work' }) + router.push({ name: 'VlogWork' }) } const handleClose = () => { - router.back() + router.push({ name: 'Action' }) } +// watch( +// () => currentStep.value, +// newVal => { +// if (newVal !== 0 && newVal !== 4) { +// visible.value = true +// } +// } +// ) + onMounted(() => { - if (route.query.key === 'video') { - currentStep.value = 1 + if (currentStep.value !== 0 && currentStep.value !== 4) { + visible.value = true } }) @@ -70,7 +84,7 @@ onMounted(() => { :style="{ backgroundImage: getBackgroundImage(IMAGE_PATHS.VLOG.TASK_CAMERA) }" > @@ -78,6 +92,23 @@ onMounted(() => { + + + + + + + + +
+ +
+
+
+import { getBackgroundImage, getStaticImage, IMAGE_PATHS } from '@/utils' +import { GuideSteps, GuideStepsItem } from '@/components/Guide' +import { ref, computed } from 'vue' +import { useRouter } from 'vue-router' +import { ImgButton } from '@/components/Button' + +const router = useRouter() + +const back = () => { + // 跳转设备准备 + router.back() +} +const steps = [ + { id: 'step1', content: '先认识 Vlog:点击右侧「1.vlog初识」继续' }, + { id: 'step2', content: '这是主要内容区域的边框' } +] +const stepIndex = ref(0) +const hiddenbar = ref(false) +const currentIndex = ref(0) // 当前显示的图片索引 +const selectedMenuIndex = ref(0) // 当前选中的菜单项索引 + +// 菜单项数据 +const menuItems = ref([ + { id: 'phone', name: '手机', disabled: false }, + { id: 'phone-stand', name: '手机支架', disabled: true }, + { id: 'bluetooth-remote', name: '蓝牙遥控器', disabled: true }, + { id: 'stabilizer', name: '稳定器', disabled: true }, + { id: 'props', name: '常用道具', disabled: true }, + { id: 'phone-lens', name: '手机辅助箱头', disabled: true } +]) + +const phoneList = ref([ + { phoneImg: getStaticImage('equipment/设备-手机1.png') }, + { phoneImg: getStaticImage('equipment/设备-手机2.png') } +]) + +const platetitleList = ref([ + { title: getStaticImage('equipment/标题-苹果.png') }, + { title: getStaticImage('equipment/标题-安卓小米.png') } +]) + +const platetextList = ref([ + { text: getStaticImage('equipment/内容-苹果.png') }, + { text: getStaticImage('equipment/内容-安卓小米.png') } +]) +const hiddenbarclick = () => { + hiddenbar.value = !hiddenbar.value +} + +// 选择菜单项 +const selectMenuItem = (index: number, item: { id: string; name: string; disabled: boolean }) => { + if (item.disabled) { + console.log(`菜单项 "${item.name}" 已被禁用`) + return + } + selectedMenuIndex.value = index +} + +// 下一张图片 +const nextPhone = () => { + if (currentIndex.value < phoneList.value.length - 1) { + currentIndex.value++ + } +} + +// 上一张图片 +const prevPhone = () => { + if (currentIndex.value > 0) { + currentIndex.value-- + } +} + +interface SimpleStep { + id: string + content: string +} + +const handleStepChange = (_step: SimpleStep | null, index: number) => { + stepIndex.value = index +} + +const currentHighlight = computed(() => + stepIndex.value === 0 ? '.img-switch-item--vlog' : '.work-border' +) + + + diff --git a/src/views/vlog/midterm-shooting/midterm/index.vue b/src/views/vlog/midterm-shooting/midterm/index.vue new file mode 100644 index 0000000..89a8f47 --- /dev/null +++ b/src/views/vlog/midterm-shooting/midterm/index.vue @@ -0,0 +1,140 @@ + + + + diff --git a/src/views/vlog/midterm-shooting/on-site/index.vue b/src/views/vlog/midterm-shooting/on-site/index.vue new file mode 100644 index 0000000..c04f364 --- /dev/null +++ b/src/views/vlog/midterm-shooting/on-site/index.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/src/views/vlog/midterm-shooting/on-site/low-angle.vue b/src/views/vlog/midterm-shooting/on-site/low-angle.vue new file mode 100644 index 0000000..0ac19d0 --- /dev/null +++ b/src/views/vlog/midterm-shooting/on-site/low-angle.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/views/vlog/midterm-shooting/on-site/random.vue b/src/views/vlog/midterm-shooting/on-site/random.vue new file mode 100644 index 0000000..629e2fa --- /dev/null +++ b/src/views/vlog/midterm-shooting/on-site/random.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/views/vlog/midterm-shooting/on-site/slider.vue b/src/views/vlog/midterm-shooting/on-site/slider.vue new file mode 100644 index 0000000..fc9231c --- /dev/null +++ b/src/views/vlog/midterm-shooting/on-site/slider.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/views/vlog/midterm-shooting/on-site/slow-shutter-day.vue b/src/views/vlog/midterm-shooting/on-site/slow-shutter-day.vue new file mode 100644 index 0000000..626184a --- /dev/null +++ b/src/views/vlog/midterm-shooting/on-site/slow-shutter-day.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/views/vlog/midterm-shooting/on-site/slow-shutter-night.vue b/src/views/vlog/midterm-shooting/on-site/slow-shutter-night.vue new file mode 100644 index 0000000..af48399 --- /dev/null +++ b/src/views/vlog/midterm-shooting/on-site/slow-shutter-night.vue @@ -0,0 +1,41 @@ + + + diff --git a/src/views/vlog/midterm-shooting/on-site/time-lapse.vue b/src/views/vlog/midterm-shooting/on-site/time-lapse.vue new file mode 100644 index 0000000..8d2c5e0 --- /dev/null +++ b/src/views/vlog/midterm-shooting/on-site/time-lapse.vue @@ -0,0 +1,157 @@ + + + diff --git a/src/views/vlog/midterm-shooting/shooting/components/StartOverlay.vue b/src/views/vlog/midterm-shooting/shooting/components/StartOverlay.vue new file mode 100644 index 0000000..074f557 --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/components/StartOverlay.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/views/vlog/midterm-shooting/shooting/composables/useCanvasCapture.ts b/src/views/vlog/midterm-shooting/shooting/composables/useCanvasCapture.ts new file mode 100644 index 0000000..81c21bc --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/composables/useCanvasCapture.ts @@ -0,0 +1,77 @@ +import { ref } from 'vue' + +interface CaptureOptions { + width: number + height: number + scale?: number + delay?: number // 截图前的等待时间(用于 UI 更新) + resultDelay?: number // 截图后显示结果的延迟 + onDraw: (ctx: CanvasRenderingContext2D) => void | Promise + onComplete?: () => void +} + +export function useCanvasCapture() { + const isLoading = ref(false) + const showModal = ref(false) + const capturedImageUrl = ref('') + + const capture = async (options: CaptureOptions) => { + if (isLoading.value) return + + const { width, height, scale = 2, delay = 50, resultDelay = 0, onDraw, onComplete } = options + + isLoading.value = true + + try { + // 等待 UI 更新 + if (delay > 0) { + await new Promise(resolve => setTimeout(resolve, delay)) + } + + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + if (!ctx) { + throw new Error('Cannot get canvas context') + } + + // 设置画布大小(高清) + canvas.width = width * scale + canvas.height = height * scale + + // 填充白色背景(防止透明) + ctx.fillStyle = '#ffffff' + ctx.fillRect(0, 0, canvas.width, canvas.height) + + // 应用缩放 + ctx.scale(scale, scale) + + // 执行自定义绘制逻辑 + await onDraw(ctx) + + capturedImageUrl.value = canvas.toDataURL('image/png') + + // 延迟显示结果 + if (resultDelay > 0) { + setTimeout(() => { + isLoading.value = false + showModal.value = true + onComplete?.() + }, resultDelay) + } else { + isLoading.value = false + showModal.value = true + onComplete?.() + } + } catch (error) { + console.error('Capture failed:', error) + isLoading.value = false + } + } + + return { + isLoading, + showModal, + capturedImageUrl, + capture + } +} diff --git a/src/views/vlog/midterm-shooting/shooting/filmingLocation-a/index.vue b/src/views/vlog/midterm-shooting/shooting/filmingLocation-a/index.vue new file mode 100644 index 0000000..ddaf8a3 --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/filmingLocation-a/index.vue @@ -0,0 +1,330 @@ + + + + + diff --git a/src/views/vlog/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts b/src/views/vlog/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts new file mode 100644 index 0000000..b3d184e --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/filmingLocation-a/useViewfinderA.ts @@ -0,0 +1,149 @@ +import { ref } from 'vue' +import { getTouchDistance, getClientPosition } from '../utils/touchGestures' + +/** + * 手机拍照模式的取景逻辑 + * 支持缩放和拖拽功能 + */ +export function useViewfinderA() { + // 缩放限制 + const MIN_ZOOM = 1 + const MAX_ZOOM = 3 + + // 缩放级别 (保留用于点击按钮) + const zoomLevels = [3, 2, 1] + const currentZoom = ref(1) + const isCapturing = ref(false) // 拍照闪光效果 + + // 拖拽相关状态 + const isDragging = ref(false) + const dragStartX = ref(0) + const dragStartY = ref(0) + const offsetX = ref(0) + const offsetY = ref(0) + const currentOffsetX = ref(0) + const currentOffsetY = ref(0) + + // 双指缩放相关 + const isPinching = ref(false) + const pinchStartDistance = ref(0) + const pinchStartZoom = ref(1) + + // 计算边界限制 + const limitOffset = () => { + const containerWidth = 871 + const containerHeight = 420 + const scale = currentZoom.value + + // 计算图片缩放后的尺寸 + const scaledWidth = containerWidth * scale + const scaledHeight = containerHeight * scale + + // 计算最大偏移量(图片边缘不能超出容器中心) + const maxOffsetX = (scaledWidth - containerWidth) / 2 + const maxOffsetY = (scaledHeight - containerHeight) / 2 + + // 限制偏移量 + currentOffsetX.value = Math.max(-maxOffsetX, Math.min(maxOffsetX, currentOffsetX.value)) + currentOffsetY.value = Math.max(-maxOffsetY, Math.min(maxOffsetY, currentOffsetY.value)) + } + + // 设置缩放级别 + const setZoom = (zoom: number) => { + currentZoom.value = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, zoom)) + limitOffset() + } + + // 鼠标滚轮缩放 + const handleWheel = (e: WheelEvent) => { + e.preventDefault() + // 滚轮向下是正数(缩小),向上是负数(放大) + const delta = -e.deltaY * 0.001 + const newZoom = currentZoom.value + delta + setZoom(newZoom) + } + + // 开始拖拽 / 触摸 + const startDrag = (e: MouseEvent | TouchEvent) => { + // 双指缩放 + if ('touches' in e && e.touches.length === 2) { + isPinching.value = true + isDragging.value = false + pinchStartDistance.value = getTouchDistance(e.touches[0], e.touches[1]) + pinchStartZoom.value = currentZoom.value + e.preventDefault() + return + } + + // 单指拖拽 + if ('touches' in e && e.touches.length !== 1) return + + isDragging.value = true + isPinching.value = false + + const { x: clientX, y: clientY } = getClientPosition(e) + + dragStartX.value = clientX + dragStartY.value = clientY + offsetX.value = currentOffsetX.value + offsetY.value = currentOffsetY.value + } + + // 拖拽中 / 缩放中 + const onDrag = (e: MouseEvent | TouchEvent) => { + // 处理双指缩放 + if (isPinching.value && 'touches' in e && e.touches.length === 2) { + const currentDistance = getTouchDistance(e.touches[0], e.touches[1]) + const scale = currentDistance / pinchStartDistance.value + setZoom(pinchStartZoom.value * scale) + e.preventDefault() + return + } + + if (!isDragging.value) return + + const { x: clientX, y: clientY } = getClientPosition(e) + + const deltaX = clientX - dragStartX.value + const deltaY = clientY - dragStartY.value + + currentOffsetX.value = offsetX.value + deltaX + currentOffsetY.value = offsetY.value + deltaY + + limitOffset() + + // 防止滚动 + if (e.cancelable) { + e.preventDefault() + } + } + + // 结束拖拽 + const endDrag = () => { + isDragging.value = false + isPinching.value = false + } + + // 计算transform样式 + const getTransform = () => { + return `translate(${currentOffsetX.value}px, ${currentOffsetY.value}px) scale(${currentZoom.value})` + } + + return { + // 状态 + zoomLevels, + currentZoom, + isCapturing, + isDragging, + currentOffsetX, + currentOffsetY, + + // 方法 + setZoom, + startDrag, + onDrag, + endDrag, + handleWheel, + getTransform + } +} diff --git a/src/views/vlog/midterm-shooting/shooting/filmingLocation-b/index.vue b/src/views/vlog/midterm-shooting/shooting/filmingLocation-b/index.vue new file mode 100644 index 0000000..ea8f711 --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/filmingLocation-b/index.vue @@ -0,0 +1,289 @@ + + + diff --git a/src/views/vlog/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts b/src/views/vlog/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts new file mode 100644 index 0000000..9e4d4cd --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/filmingLocation-b/useViewfinderB.ts @@ -0,0 +1,291 @@ +import { ref, onMounted, onUnmounted } from 'vue' +import { getTouchDistance, getTouchCenter, getClientPosition } from '../utils/touchGestures' + +// 设计稿尺寸常量 +const DESIGN_WIDTH = 1920 +const DESIGN_HEIGHT = 1080 + +/** + * 自由取景框模式的逻辑 + * 支持拖动、四角调整大小、双指缩放 + */ +export function useViewfinderB() { + // 取景框相关 + const viewfinderContainer = ref() + const viewfinderBox = ref() + const viewfinder = ref({ + x: 660, // (1920 - 600) / 2 = 660,居中 + y: 340, // (1080 - 400) / 2 = 340,居中 + width: 600, + height: 400 + }) + + // 拖动相关状态 + const isDragging = ref(false) + const dragStart = ref({ x: 0, y: 0 }) + + // 调整大小相关状态 + const isResizing = ref(false) + const resizeCorner = ref<'nw' | 'ne' | 'sw' | 'se' | null>(null) + const resizeStart = ref({ + x: 0, + y: 0, + boxX: 0, + boxY: 0, + boxWidth: 0, + boxHeight: 0 + }) + + // 双指缩放相关状态 + const isPinching = ref(false) + const pinchStart = ref({ + distance: 0, + centerX: 0, + centerY: 0, + boxWidth: 0, + boxHeight: 0, + boxX: 0, + boxY: 0 + }) + + // 将屏幕坐标转换为缩放后的本地坐标 + const convertClientToLocal = (clientX: number, clientY: number) => { + if (!viewfinderContainer.value) return { x: clientX, y: clientY } + + const containerRect = viewfinderContainer.value.getBoundingClientRect() + + // 计算缩放比例 + const scaleX = containerRect.width / DESIGN_WIDTH + const scaleY = containerRect.height / DESIGN_HEIGHT + + // 计算相对于容器的坐标,并转换为设计稿坐标系 + const localX = (clientX - containerRect.left) / scaleX + const localY = (clientY - containerRect.top) / scaleY + + return { x: localX, y: localY } + } + + // 开始拖动取景框 + const startDrag = (e: MouseEvent | TouchEvent) => { + if (isResizing.value || isPinching.value) return + + // 检测双指触摸 + if ('touches' in e && e.touches.length === 2) { + // 双指缩放 + isPinching.value = true + const touch1 = e.touches[0] + const touch2 = e.touches[1] + + pinchStart.value = { + distance: getTouchDistance(touch1, touch2), + centerX: (touch1.clientX + touch2.clientX) / 2, + centerY: (touch1.clientY + touch2.clientY) / 2, + boxWidth: viewfinder.value.width, + boxHeight: viewfinder.value.height, + boxX: viewfinder.value.x, + boxY: viewfinder.value.y + } + e.preventDefault() + return + } + + // 单指拖动 + if ('touches' in e && e.touches.length !== 1) return + + isDragging.value = true + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + dragStart.value = { + x: localPos.x - viewfinder.value.x, + y: localPos.y - viewfinder.value.y + } + + e.preventDefault() + } + + // 开始调整大小 + const startResize = (corner: 'nw' | 'ne' | 'sw' | 'se', e: MouseEvent | TouchEvent) => { + isResizing.value = true + resizeCorner.value = corner + + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + resizeStart.value = { + x: localPos.x, + y: localPos.y, + boxX: viewfinder.value.x, + boxY: viewfinder.value.y, + boxWidth: viewfinder.value.width, + boxHeight: viewfinder.value.height + } + + e.preventDefault() + } + + // 处理移动 + const handleMove = (e: MouseEvent | TouchEvent) => { + if (!isDragging.value && !isResizing.value && !isPinching.value) return + + // 处理双指缩放 + if (isPinching.value && 'touches' in e && e.touches.length === 2) { + const touch1 = e.touches[0] + const touch2 = e.touches[1] + + const currentDistance = getTouchDistance(touch1, touch2) + const scale = currentDistance / pinchStart.value.distance + + // 计算新的尺寸 + let newWidth = pinchStart.value.boxWidth * scale + let newHeight = pinchStart.value.boxHeight * scale + + const minSize = 200 + + // 限制最小和最大尺寸 + newWidth = Math.max(minSize, Math.min(newWidth, DESIGN_WIDTH)) + newHeight = Math.max(minSize, Math.min(newHeight, DESIGN_HEIGHT)) + + // 保持中心点不变,计算新的位置 + const center = getTouchCenter(touch1, touch2) + const localCenter = convertClientToLocal(center.x, center.y) + + let newX = localCenter.x - newWidth / 2 + let newY = localCenter.y - newHeight / 2 + + // 边界检测 + newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - newWidth)) + newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - newHeight)) + + viewfinder.value.width = newWidth + viewfinder.value.height = newHeight + viewfinder.value.x = newX + viewfinder.value.y = newY + + e.preventDefault() + return + } + + const { x: clientX, y: clientY } = getClientPosition(e) + + const localPos = convertClientToLocal(clientX, clientY) + + if (isDragging.value && viewfinderContainer.value) { + // 拖动取景框 + let newX = localPos.x - dragStart.value.x + let newY = localPos.y - dragStart.value.y + + // 边界检测 - 使用设计稿尺寸 + newX = Math.max(0, Math.min(newX, DESIGN_WIDTH - viewfinder.value.width)) + newY = Math.max(0, Math.min(newY, DESIGN_HEIGHT - viewfinder.value.height)) + + viewfinder.value.x = newX + viewfinder.value.y = newY + } else if (isResizing.value && viewfinderContainer.value) { + // 调整取景框大小 + const deltaX = localPos.x - resizeStart.value.x + const deltaY = localPos.y - resizeStart.value.y + + const minSize = 200 // 最小尺寸 + + if (resizeCorner.value === 'se') { + // 右下角 - 使用设计稿尺寸限制 + viewfinder.value.width = Math.max( + minSize, + Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) + ) + viewfinder.value.height = Math.max( + minSize, + Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) + ) + } else if (resizeCorner.value === 'sw') { + // 左下角 - 使用设计稿尺寸限制 + const newWidth = resizeStart.value.boxWidth - deltaX + const newX = resizeStart.value.boxX + deltaX + if (newWidth >= minSize && newX >= 0) { + viewfinder.value.x = newX + viewfinder.value.width = newWidth + } + viewfinder.value.height = Math.max( + minSize, + Math.min(resizeStart.value.boxHeight + deltaY, DESIGN_HEIGHT - viewfinder.value.y) + ) + } else if (resizeCorner.value === 'ne') { + // 右上角 - 使用设计稿尺寸限制 + const newHeight = resizeStart.value.boxHeight - deltaY + const newY = resizeStart.value.boxY + deltaY + if (newHeight >= minSize && newY >= 0) { + viewfinder.value.y = newY + viewfinder.value.height = newHeight + } + viewfinder.value.width = Math.max( + minSize, + Math.min(resizeStart.value.boxWidth + deltaX, DESIGN_WIDTH - viewfinder.value.x) + ) + } else if (resizeCorner.value === 'nw') { + // 左上角 - 使用设计稿尺寸限制 + const newWidth = resizeStart.value.boxWidth - deltaX + const newHeight = resizeStart.value.boxHeight - deltaY + const newX = resizeStart.value.boxX + deltaX + const newY = resizeStart.value.boxY + deltaY + + if (newWidth >= minSize && newX >= 0) { + viewfinder.value.x = newX + viewfinder.value.width = newWidth + } + if (newHeight >= minSize && newY >= 0) { + viewfinder.value.y = newY + viewfinder.value.height = newHeight + } + } + } + + e.preventDefault() + } + + // 结束拖动或调整 + const handleEnd = () => { + isDragging.value = false + isResizing.value = false + isPinching.value = false + resizeCorner.value = null + } + + // 挂载时添加全局事件监听 + onMounted(() => { + document.addEventListener('mousemove', handleMove) + document.addEventListener('mouseup', handleEnd) + document.addEventListener('touchmove', handleMove) + document.addEventListener('touchend', handleEnd) + }) + + // 卸载时移除事件监听 + onUnmounted(() => { + document.removeEventListener('mousemove', handleMove) + document.removeEventListener('mouseup', handleEnd) + document.removeEventListener('touchmove', handleMove) + document.removeEventListener('touchend', handleEnd) + }) + + return { + // Refs + viewfinderContainer, + viewfinderBox, + + // 状态 + viewfinder, + isDragging, + isResizing, + isPinching, + + // 方法 + startDrag, + startResize, + + // 常量 + DESIGN_WIDTH, + DESIGN_HEIGHT + } +} diff --git a/src/views/vlog/midterm-shooting/shooting/index.vue b/src/views/vlog/midterm-shooting/shooting/index.vue new file mode 100644 index 0000000..01ddc5f --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/index.vue @@ -0,0 +1,222 @@ + + + diff --git a/src/views/vlog/midterm-shooting/shooting/utils/touchGestures.ts b/src/views/vlog/midterm-shooting/shooting/utils/touchGestures.ts new file mode 100644 index 0000000..ce27a3a --- /dev/null +++ b/src/views/vlog/midterm-shooting/shooting/utils/touchGestures.ts @@ -0,0 +1,47 @@ +/** + * 触摸手势工具函数 + * 用于处理触摸事件的通用计算 + */ + +/** + * 计算两个触摸点之间的距离 + * @param touch1 第一个触摸点 + * @param touch2 第二个触摸点 + * @returns 两点之间的欧氏距离 + */ +export const getTouchDistance = (touch1: Touch, touch2: Touch): number => { + const dx = touch1.clientX - touch2.clientX + const dy = touch1.clientY - touch2.clientY + return Math.sqrt(dx * dx + dy * dy) +} + +/** + * 计算两个触摸点的中心坐标 + * @param touch1 第一个触摸点 + * @param touch2 第二个触摸点 + * @returns 中心点坐标 {x, y} + */ +export const getTouchCenter = (touch1: Touch, touch2: Touch) => { + return { + x: (touch1.clientX + touch2.clientX) / 2, + y: (touch1.clientY + touch2.clientY) / 2 + } +} + +/** + * 从鼠标或触摸事件中获取客户端坐标 + * @param e 鼠标事件或触摸事件 + * @returns 客户端坐标 {x, y} + */ +export const getClientPosition = (e: MouseEvent | TouchEvent) => { + if ('touches' in e && e.touches.length > 0) { + return { + x: e.touches[0].clientX, + y: e.touches[0].clientY + } + } + return { + x: (e as MouseEvent).clientX, + y: (e as MouseEvent).clientY + } +} diff --git a/src/views/vlog/practice/index.vue b/src/views/vlog/practice/index.vue new file mode 100644 index 0000000..61dbbf3 --- /dev/null +++ b/src/views/vlog/practice/index.vue @@ -0,0 +1,357 @@ + + + + + diff --git a/src/views/vlog/practice/my-notes/index.vue b/src/views/vlog/practice/my-notes/index.vue new file mode 100644 index 0000000..7e4a09c --- /dev/null +++ b/src/views/vlog/practice/my-notes/index.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/src/views/vlog/practice/video/index.vue b/src/views/vlog/practice/video/index.vue new file mode 100644 index 0000000..4120c50 --- /dev/null +++ b/src/views/vlog/practice/video/index.vue @@ -0,0 +1,129 @@ + + + diff --git a/src/views/vlog/script-writing/doubao.vue b/src/views/vlog/script-writing/doubao.vue new file mode 100644 index 0000000..e69de29 diff --git a/src/views/vlog/script-writing/index.vue b/src/views/vlog/script-writing/index.vue new file mode 100644 index 0000000..29851f1 --- /dev/null +++ b/src/views/vlog/script-writing/index.vue @@ -0,0 +1,88 @@ + + diff --git a/src/views/prepare/script-writing/sample.vue b/src/views/vlog/script-writing/sample.vue similarity index 96% rename from src/views/prepare/script-writing/sample.vue rename to src/views/vlog/script-writing/sample.vue index ff3c109..ea9b6f4 100644 --- a/src/views/prepare/script-writing/sample.vue +++ b/src/views/vlog/script-writing/sample.vue @@ -1,3 +1,57 @@ + -