WWW.YOUINFO.SITE
标签聚合 uniapp

/tag/uniapp

v2ex · 2026-05-15 15:37:08+08:00 · tech

目前看到网上的方案基本上都是先转到 figma 然后再用 MCP 转成前端代码,并且质量也不高。 所以我用了一周的时间做了个工具,能够自动裁剪和重绘 UI 设计图上的元素,高质量的把 UI 图转成前端代码。 可以直接看对比效果(一次直出没有任何调整): 左侧是还原后的效果,右侧是 UI 图片。 操作非常简单,上传参考图片,然后看看有没有要调整的标注,没有直接点击“生成资产”就可以。 最后你可以把资源包发给 coding agent ,然后直接引用 prompt.md (资源包里自带)就可以直接转代码了。 开源地址: https://github.com/shouzi23333-rgb/AiClip 欢迎感兴趣的同学试试~

v2ex · 2026-05-15 15:26:42+08:00 · tech

目前看到网上的方案基本上都是先转到 figma 然后再用 MCP 转成前端代码,并且质量也不高。 所以我用了一周的时间做了个工具,能够自动裁剪和重绘 UI 设计图上的元素,高质量的把 UI 图转成前端代码。 可以直接看对比效果(一次直出没有任何调整): 左侧是还原后的效果,右侧是 UI 图片。 操作非常简单,上传参考图片,然后看看有没有要调整的标注,没有直接点击“生成资产”就可以。 最后你可以把资源包发给 coding agent ,然后直接引用 prompt.md (资源包里自带)就可以直接转代码了。 开源地址: https://github.com/shouzi23333-rgb/AiClip 欢迎感兴趣的同学试试~

v2ex · 2026-05-15 14:58:02+08:00 · tech

目前看到网上的方案基本上都是先转到 figma 然后再用 MCP 转成前端代码,并且质量也不高。 所以我用了一周的时间做了个工具,能够自动裁剪和重绘 UI 设计图上的元素,高质量的把 UI 图转成前端代码。 可以直接看对比效果(一次直出没有任何调整): 左侧是还原后的效果,右侧是 UI 图片。 操作非常简单,上传参考图片,然后看看有没有要调整的标注,没有直接点击“生成资产”就可以。 最后你可以把资源包发给 coding agent ,然后直接引用 prompt.md (资源包里自带)就可以直接转代码了。 开源地址: https://github.com/shouzi23333-rgb/AiClip 欢迎感兴趣的同学试试~

v2ex · 2026-05-15 14:20:03+08:00 · tech

目前看到网上的方案基本上都是先转到 figma 然后再用 MCP 转成前端代码,并且质量也不高。 所以我用了一周的时间做了个工具,能够自动裁剪和重绘 UI 设计图上的元素,高质量的把 UI 图转成前端代码。 可以直接看对比效果(一次直出没有任何调整): 左侧是还原后的效果,右侧是 UI 图片。 操作非常简单,上传参考图片,然后看看有没有要调整的标注,没有直接点击“生成资产”就可以。 最后你可以把资源包发给 coding agent ,然后直接引用 prompt.md (资源包里自带)就可以直接转代码了。 开源地址: https://github.com/shouzi23333-rgb/AiClip 欢迎感兴趣的同学试试~

v2ex · 2026-05-15 14:20:03+08:00 · tech

目前看到网上的方案基本上都是先转到 figma 然后再用 MCP 转成前端代码,并且质量也不高。 所以我用了一周的时间做了个工具,能够自动裁剪和重绘 UI 设计图上的元素,高质量的把 UI 图转成前端代码。 可以直接看对比效果(一次直出没有任何调整): 左侧是还原后的效果,右侧是 UI 图片。 操作非常简单,上传参考图片,然后看看有没有要调整的标注,没有直接点击“生成资产”就可以。 最后你可以把资源包发给 coding agent ,然后直接引用 prompt.md (资源包里自带)就可以直接转代码了。 开源地址: https://github.com/shouzi23333-rgb/AiClip 欢迎感兴趣的同学试试~

LinuxDo 最新话题 · 2026-05-13 13:19:45+08:00 · tech

最近打算去开发一个小程序,第一个问题就是在采用的具体的技术栈和模式,我当时的第一方案是,采用nextjs去开发web,然后用容器去做嵌入小程序的模式。 后来一直和微信小程序原生的这个有冲突,但是我直接去开发小程序也不行吧,太小众了这玩意,我还是希望就是可以有多套的这个,web+小程序一体的。 完了我查了一下,uniapp他们说是目前的这个比较好的方案,比较主流的方案,不知道这个到底可行不可行 5 个帖子 - 5 位参与者 阅读完整话题

linux.do · 2026-04-26 23:57:03+08:00 · tech

前景:朋友用uniapp开发的app应用,然后抱怨云打包一直排队,大概最近ai的应用太多了,导致排队太多太久了,问我有什么方法可以手动打包的,之前没有接触过安卓打包,然后用ai搜了下可以实现,然后感觉这也是一个商机嘛,自己建一个打包的平台,然后研究了下。 整个流程 Node.js CLI工具 ↓ 调用 Capacitor ↓ 自动生成 Android 项目 ↓ 调用 Gradle 打包 APK 安装工具 1. 安装commandlinetools - 安卓打包需要的环境 - 设置环境变量在cmdline-tools上层目录设ANDROID_HOME - 安装组件sdkmanager "platform-tools" "platforms;android-33" "build-tools;33.0.2" - sdkmanager --licenses 一路y确认 2. 安装capacitor - 前端资源转换成安卓源码 - npm install @capacitor/core @capacitor/cli 3. 安装gradlew - 用来打包安卓应用 操作步骤 1. 先用npm打包 - 我这边用的uniapp,npm run build:h5 2. 用capacitor初始化配置 - npx cap init - 生成安卓应用的基础信息 - 然后我看有些有执行前端资源路径的,我的版本没有看到,然后去capacitor.config.json这个文件中设置webDir,设置前端打包以后的路径,默认是dist,如果静态地址不是这个需要修改 - npx cap android * 生成对应的android文件夹 - npx cap sync android * 把前端资源同步至android包内 **这边如果前端修改了,只需要npx cap sync重新同步至安卓包就可以,其他的可以不做操作** 用gradle打包安卓应用 - 到android,执行**gradlew assembleRelease/gradlew assembleDebug**,release好像是要验证应用签名的,我这边就没有试 - 然后文件就会在**android\app\build\outputs\apk\debug\xxx.apk** 这个目录中,如果是release会在apk下release文件夹中 打包都时候有可能jdk版本不兼容问题 添加configurations.all和ext这两项 我用的新的版本,说jdk17就可以了,但是老是报版本低,后面系统jdk升到21就可以 还有打包的时候有些插件打完以后会有多个版本的kotlin,需要兼容下 // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.7.2' classpath 'com.google.gms:google-services:4.4.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } apply from: "variables.gradle" ext { kotlin_version = '1.8.22' } allprojects { configurations.all { resolutionStrategy { force "org.jetbrains.kotlin:kotlin-stdlib:1.8.22" force "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22" force "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22" } } repositories { google() mavenCentral() } } task clean(type: Delete) { delete rootProject.buildDir } 把apk复制到模拟器或者安卓真机中,或者执行 npx cap run android 可以直接运行到模拟器中 注意事项 如果是用uniapp的uni.request网络请求的话,有可能是用不了,换成fetch就可以,之前封装的uni.request请求工具类,怎么试都打不到后端,后面试了下fetch可以打到后端,但是回报跨域问题,后面后端开启支持跨域就可以了,查了下uniapp是用原生的okhttp去请求做了封装,然后capacitor是用XMLHttpRequest去做请求的。如果换成fetch需要改工具类,有些不规范的接口也需要调整,比如用post请求,但是参数放到url连接中的也会报错,这个请求必须要有body,然后自己需要改接口,改成formdata格式后面就可以。 如果后端http请求,可以添加capacitor配置文件中添加capacitor配置文件中添加或者AndroidManifest.xml中添加android:usesCleartextTraffic=“true”,但是我怎么试都没用,不知道怎么回事 capacitor.config.json配置文件中添加 "server": { "cleartext": true } ------- AndroidManifest.xml文件中添加 <application android:usesCleartextTraffic="true" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> 总结 可以用capacitor来本地打包成安卓应用 打包的时候jdk版本会有要求,以及插件中kotlin会有版本冲突 网络请求不兼容,需要手动修改请求工具类满足请求,可以用fetch或者axios,建议开发的时候用axios开发 安卓对http请求禁用,http网络请求需要混合模式开始,也没找到方法,后面升到https 可以打包简单的安卓应用,也没有试调用系统的功能入相机或其他功能 6 个帖子 - 4 位参与者 阅读完整话题

linux.do · 2026-04-18 22:05:59+08:00 · tech

<script setup> import { ref } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; import request from '@/utils/request'; import { getFiles, getRandomFile, getFileArray } from '@/utils/yangUtils'; const imageSuffix = ['png', 'jpg', 'jpeg']; const videoSuffix = ['mp4', 'avi', 'mov']; const imageNum = ref(0); const videoNum = ref(0); // 获取父级传入的数据 const props = defineProps(["modelValue", "limit", "message", "isDelete", "isAdd"]); // 定义 emits const emit = defineEmits(['update:modelValue']); // const listData = ref(["https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417192542-d3ea46.png", "https://yxdjpw.oss-cn-beijing.aliyuncs.com/uploadDefault/20260417194449-bfb1ba.mp4"]); const listData = ref([]) const onSelectFile = async () => { let result; // #ifdef APP || APP-PLUS || MP-WEIXIN || MP-TOUTIAO || MP-LARK || MP-JD || MP-HARMONY || MP-XHS result = await uni.chooseMedia({ count: props.limit || 9, mediaType: ['image', 'video'], sourceType: ['album', 'camera'], maxDuration: '30s' }) // #endif // #ifdef WEB || H5 result = await uni.chooseFile({ count: props.limit || 9, type: 'all', }) // #endif let arr = []; console.log(result); for (let filePath of result.tempFiles) { // 判断文件是否超出10M if (filePath.size > 10 * 1024 * 1024) { uni.showModal({ title: '提示', content: '文件大小不能超过10M!', showCancel: true, }) return; } if (imageSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) { imageNum.value++; } else if (videoSuffix.includes(filePath.name.replaceAll('"', '').split('.').pop()?.toLowerCase())) { // 判断之前有没有上传过视频或者图片 if (imageNum.value >= 1 || videoNum.value >= 1) { uni.showModal({ title: '提示', content: '图片和视频不能同时上传,并且视频只能上传一个!', showCancel: true, }) return; } videoNum.value++; } const data = await request("/upload/uploadFile", filePath.path, "post"); arr.push(getFileArray(data)[0]); } listData.value = listData.value.concat(arr); // 将数据返回出去 emit("update:modelValue", listData.value.join(",")); } // 删除 const onDelete = (index) => { // 1. 先拿到要删除的项(必须在 splice 之前拿!) const deletedItem = listData.value[index]; // 2. 判断类型,更新计数 const ext = deletedItem.split('.').pop()?.toLowerCase(); if (imageSuffix.includes(ext)) { imageNum.value--; } else if (videoSuffix.includes(ext)) { videoNum.value--; } // 3. 再删除元素 listData.value.splice(index, 1); // 4. 更新双向绑定 emit("update:modelValue", listData.value.join(",")); } // 查看 const onView = (item) => { } // 类型判断 const isSuffix = () => { return listData.value.some(item => { // 获取后缀(转小写,避免大小写问题) const ext = item.split('.').pop()?.toLowerCase() return videoSuffix.includes(ext) }) } </script> <template> <view> <view class="header"> <view v-if="props.message" class="message">{{ props.message }}</view> <view class="numCount">{{ `${listData.length}/${props.limit || 9}` }}</view> </view> <view class="image-grid" :class="isSuffix() ? 'grid-video' : ''"> <template v-for="(item, index) in listData" :key="index"> <view class="grid-item grid-item-content" :class="isSuffix() ? 'grid-item-video' : ''" @click="onView(item)"> <image v-if="imageSuffix.includes(item.split('.').pop())" class="img" :src="item" mode="aspectFill" /> <video v-else-if="videoSuffix.includes(item.split('.').pop())" class="video" :src="item" /> <view class="grid-item-delete" v-if="(isAdd ?? true)" @click="onDelete(index)"><uni-icons class="icon-delete" type="closeempty" color="#D3D4D6" size="24" /></view> </view> </template> <view class="grid-item icon-add" v-if="(isAdd ?? true) && !isSuffix() && listData.length < (props.limit ?? 9)" @click="onSelectFile()"> <uni-icons type="plusempty" size="60" color="#F1F1F1"></uni-icons> </view> </view> </view> </template> <style lang="scss" scoped> .header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20rpx; } .image-grid { display: grid; grid-template-columns: repeat(3, 1fr); /* 一行3个,自动均分 */ gap: 20rpx; /* 格子间距 */ box-sizing: border-box; } .grid-item { width: 240rpx; height: 240rpx; } .grid-video { grid-template-columns: repeat(1, 1fr) !important; } .grid-item-video { width: 100%; height: 400rpx; } .numCount { display: flex; flex-direction: row-reverse; font-size: 26rpx; } .img, .video { width: 100%; height: 100%; } .grid-item-content { position: relative; } .icon-add { background: #FFFFFF; line-height: 240rpx; text-align: center; border: 1rpx solid #EEEEEE; border-radius: 6rpx; } .grid-item-delete { position: absolute; top: 0rpx; right: 0rpx; z-index: 1; } </style> 方法解析 文件后端返回的是 /api/upload/xxx.png getFiles 将图片拼接成正常能访问的 比如 http://域名.com/api/upload/xxx.png getRandomFile 是随机访问 getFileArray 是拼接成数组 limit → 最多上传数量 默认为9 message → 提示 比如请上传视频 可不填 isDelete → 是否可以删除重新上传 默认为true isAdd → 是否可以新增,比如有些地方可以直接回显 比如产品的图片,但是你不想让它显示新增的框 可以为false 默认为true // 文件加载 export const getFiles = (url) => { if (!url) { return ""; } if (url.startsWith("http://") || url.startsWith("https://")) { return cleanString(url); } else { return baseURL + cleanString(url); } }; // 随机加载文件 export const getRandomFile = (url) => { if (!url) { return ""; } const urls = url.split(","); const randomIndex = Math.floor(Math.random() * urls.length); const selectedUrl = urls[randomIndex]; if ( cleanString(selectedUrl).startsWith("http://") || cleanString(selectedUrl).startsWith("https://") ) { return cleanString(selectedUrl); } else { return baseURL + cleanString(selectedUrl); } }; export const getFileArray = (url) => { if (!url) { return []; } let baseUrl = []; url.split(",").forEach((v) => { if ( cleanString(v).startsWith("http://") || cleanString(v).startsWith("https://") ) { baseUrl.push(cleanString(v)); } else { baseUrl.push(baseURL + cleanString(v)); } }); console.log(baseUrl); return baseUrl; }; 请求就是 uni.request的请求 你们按照你们自己的改 当前只支持H5 你们需要的话 我会再更新 如果上传了视频就直接独占一行 并且不能上传其他视频和图片 仿照的是朋友圈 其他页面调用就是 比如文件名叫uploadFile.vue吧 import UpLoadFile from '@/components/uploadFile.vue' // 页面代码 <UpLoadFile v-model="form.media" /> 第一次封装 还有些问题 我会持续更新的 1 个帖子 - 1 位参与者 阅读完整话题