零基础鸿蒙应用开发第三十四节:MVVM架构下的商品管理登录页 - 鸿蒙

news/2026/1/25 12:17:14/文章来源:https://www.cnblogs.com/san-xiu/p/19529138

零基础鸿蒙应用开发学习计划表

【学习目标】

  1. 掌握鸿蒙工程化开发的核心规范:静态工具类的统一导入与使用,单例模式ViewModel的设计与应用(核心服务于MVVM分层解耦);
  2. 深刻理解MVVM分层核心思想:明确数据模型(Model)、视图(View)、视图模型(ViewModel)的职责边界,并落地到登录功能开发中;
  3. 掌握基于MVVM的登录功能完整实现流程:正则校验、本地JSON数据读取、账号密码对比、页面路由跳转、登录状态持久化与恢复;
  4. 深化对ArkTS语法的综合应用:异步逻辑(async/await)、上下文(Context)、AppStorage状态管理的正确使用(为MVVM层间交互提供技术支撑);
  5. 具备MVVM架构下的工程问题排查能力:解决导入路径错误、数据同步异常、路由配置失效、单例方法调用异常等常见问题。

【学习重点】

  1. MVVM分层设计落地(核心)
    • Model层:极简的接口设计(Account/ValidateResult),仅承载数据、无任何业务逻辑;
    • ViewModel层:单例模式封装所有登录业务逻辑(校验+数据交互+状态存储/读取),作为View和Model的中间层;
    • View层:仅负责UI渲染和用户交互,通过调用ViewModel方法完成业务操作,不包含任何核心逻辑;
  2. 核心功能实现:正则校验(手机号+密码)、本地JSON异步读取、router页面跳转、登录状态持久化(沙箱+AppStorage)、启动页自动校验登录状态;
  3. 常见问题解决:Context导入与使用、AppStorage状态同步、异步异常捕获(try/catch)、沙箱文件读写、单例方法的正确调用;
  4. 工程化细节:统一常量管理、工具类复用、目录结构设计,区分rawfile(静态预设数据)和沙箱(动态持久化数据)的使用场景。

一、工程准备

  1. 复制第二十九节项目ClassObjectDemo_8,重命名为ClassObjectDemo_9
  2. 将上一节中的JsonUtil.ets拷贝到utils目录下,并补充沙箱文件写入方法;
  3. 新建页面:
    • 新建GoodsPage.ets:通过New→Page→Empty Page创建,文件名称填写GoodsPage;将原Index.ets内容迁移至此并修改结构体名为struct GoodsPage
    • 删除原Index.ets,重新创建新的Index.ets作为启动引导页;
    • 新建LoginPage.ets:通过New→Page→Empty Page创建,作为登录页面;
  4. 新增constants目录及AppStorageKey.ets全局常量文件;
  5. 项目完整目录(聚焦核心文件,贴合MVVM分层):
ClassObjectDemo_9/
├── entry/
│   ├── src/
│   │   ├── main/
│   │   │   ├── ets/
│   │   │   │   ├── constants/              # 应用全局常量(跨层复用)
│   │   │   │   │   ├── AppStorageKey.ets   # 登录状态常量
│   │   │   │   ├── utils/                  # 通用工具类(ViewModel层依赖)
│   │   │   │   │   ├── JsonUtil.ets        # JSON读取/写入工具类
│   │   │   │   │   └── ValidationUtil.ets  # 正则校验工具类
│   │   │   │   ├── model/                  # Model层:仅定义数据结构,无业务逻辑
│   │   │   │   │   └── login/              # 登录业务子模型
│   │   │   │   │       ├── Account.ets     # 账号数据模型
│   │   │   │   │       └── ValidateResult.ets # 验证结果数据模型
│   │   │   │   ├── viewmodel/              # ViewModel层:封装所有业务逻辑
│   │   │   │   │   └── LoginViewModel.ets  # 登录业务视图模型(单例)
│   │   │   │   ├── pages/                  # View层:仅负责UI交互
│   │   │   │   │   ├── Index.ets           # 启动引导页(判断登录状态)
│   │   │   │   │   ├── LoginPage.ets       # 登录页(UI交互)
│   │   │   │   │   └── GoodsPage.ets       # 商品管理页
│   │   │   ├── resources/                  # 资源模块
│   │   │   │   ├── base/profile/
│   │   │   │   │   └── main_pages.json     # 页面路由配置
│   │   │   │   └── rawfile/                # 本地静态JSON目录(Model层数据源)
│   │   │   │       └── user_info.json      # 预设登录账号数据

二、全局常量

2.1 AppStorageKey.ets(登录状态常量)

// constants/AppStorageKey.ets
/*** 全局AppStorage键值常量:统一管理状态标识,避免硬编码* MVVM场景下:用于View层和ViewModel层的状态同步*/
class AppStorageKey {/** 登录状态标识 */static readonly IS_LOGIN: string = 'IS_LOGIN';
}export default AppStorageKey;

三、工具类模块

3.1 JsonUtil.ets 复用上一节未变更不展示。

3.2 ValidationUtil.ets(正则校验工具)

// utils/ValidationUtil.ets
/*** 正则校验工具类:专注登录场景的格式校验* MVVM场景下:作为ViewModel层的工具依赖,仅提供校验能力,不参与业务决策*/
export class ValidationUtil {/*** 校验手机号(11位数字,以13-9开头)* @param phone 手机号字符串* @returns 校验结果(true=合法)*/public static validatePhone(phone: string): boolean {const phoneReg = /^1[3-9]\d{9}$/;return phoneReg.test(phone);}/*** 校验密码(8-20位,含数字+字母,区分大小写)* @param password 密码字符串* @returns 校验结果(true=合法)*/public static validatePassword(password: string): boolean {const pwdReg = /^(?=.*\d)(?=.*[a-zA-Z]).{8,20}$/;return pwdReg.test(password);}
}

四、数据模型模块:MVVM的Model层(仅承载数据)

4.1 Account.ets(账号模型)

// model/login/Account.ets
/*** 账号模型(MVVM-Model层):仅承载登录所需核心字段,无任何业务逻辑* 职责边界:只定义数据结构,不包含校验、存储、交互等逻辑*/
export interface Account {phone: string;  // 手机号password: string; // 密码
}

4.2 ValidateResult.ets(验证结果模型)

// model/login/ValidateResult.ets
/*** 登录验证结果模型(MVVM-Model层):统一返回格式,便于View层处理* 职责边界:仅定义结果数据结构,由ViewModel层赋值,View层读取展示*/
export interface ValidateResult {success: boolean; // 验证是否通过message: string;  // 提示信息(成功/失败原因)
}

五、视图模型模块:MVVM的ViewModel层(核心业务逻辑封装)

设计成单例模式的原因:登录信息的读取、存储、退出登录等操作需全局统一状态,单例模式能保证ViewModel实例唯一,避免多实例导致的状态不一致,符合MVVM中ViewModel作为“唯一业务逻辑入口”的设计原则。

// viewmodel/LoginViewModel.etsimport { ValidationUtil } from "../utils/ValidationUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { ValidateResult } from "../model/login/ValidateResult";
import { Account } from "../model/login/Account";
import { Context } from '@kit.AbilityKit';
import AppStorageKey from "../constants/AppStorageKey";/*** 登录视图模型(MVVM-ViewModel层):单例模式* 核心职责:封装所有登录业务逻辑,作为View层和Model层的中间层* 边界:不涉及任何UI渲染,仅处理数据校验、存储、交互,通过统一格式返回结果给View层*/
export class LoginViewModel {// 静态私有实例(保证全局唯一)private static instance: LoginViewModel;// 实例级账号信息(非静态):仅在ViewModel内部维护,View层无需感知private currentAccount: Account = { phone: '', password: '' };// 私有构造函数(禁止外部new创建实例):保证单例唯一性private constructor() {}// 静态方法:获取全局唯一实例public static getInstance(): LoginViewModel {if (!LoginViewModel.instance) {LoginViewModel.instance = new LoginViewModel();}return LoginViewModel.instance;}/*** 实例方法:更新账号信息并自动清洗数据* 职责:接收View层传递的原始数据,做前置清洗,不涉及UI* @param account 页面输入的账号数据*/updateAccount(account: Account): void {this.currentAccount.phone = account.phone.trim();this.currentAccount.password = account.password.trim();}/*** 实例方法:验证登录信息(核心业务逻辑)* 职责:整合校验、数据读取、对比逻辑,返回标准化结果给View层* @param context 应用上下文* @returns 统一的验证结果(Model层结构)*/async validateLogin(context: Context): Promise<ValidateResult> {const phone = this.currentAccount.phone;const password = this.currentAccount.password;// 空值校验if (!phone) return { success: false, message: '请输入手机号' };if (!password) return { success: false, message: '请输入密码' };// 格式校验if (!ValidationUtil.validatePhone(phone)) {return { success: false, message: '手机号格式错误(11位数字,以13-9开头)' };}if (!ValidationUtil.validatePassword(password)) {return { success: false, message: '密码格式错误(8-20位,含数字+字母)' };}// 本地数据对比(模拟服务器校验)try {const accountJson: string = await JsonUtil.readRawFileJson(context, 'user_info.json');const userData: Account = JSON.parse(accountJson);const userPhone = userData.phone;const userPassword = userData.password;if (!userPhone || !userPassword) {return { success: false, message: '用户数据格式错误' };}// 账号密码匹配:核心业务判断,仅返回结果if (phone === userPhone && password === userPassword) {await this.saveLoginState(context, userData);return { success: true, message: '登录成功' };} else {return { success: false, message: '手机号或密码错误' };}} catch (error) {return { success: false, message: `登录失败:${(error as Error).message}` };}}/*** 实例方法:存储登录状态(沙箱+AppStorage)* 职责:处理登录状态持久化,完成Model层数据到存储介质的交互* @param context 应用上下文* @param account 登录成功的账号信息* @returns 存储结果*/async saveLoginState(context: Context, account: Account): Promise<boolean> {try {// 仅限于基础阶段我们演示登录。基础阶段用沙箱文件是为了降低学习难度,preferences用户首选项存储数据我们还没接触;await JsonUtil.writeSandboxJson(context, 'user_account.json', account);// AppStorage为系统内置,直接使用:实现ViewModel层到View层的状态同步AppStorage.setOrCreate(AppStorageKey.IS_LOGIN, true);console.log(`用户登录成功,信息已存储:${JSON.stringify(account)}`);return true;} catch (error) {console.error(`存储登录状态失败:${(error as Error).message}`);return false;}}/*** 实例方法:读取登录状态(从沙箱恢复)* 职责:初始化登录状态,为View层的页面跳转提供数据支撑* @param context 应用上下文*/async readLoginState(context: Context): Promise<void> {try {const accountJson = await JsonUtil.readSandboxJson(context, 'user_account.json');if (accountJson) {// AppStorage为系统内置,直接使用:同步状态到View层AppStorage.setOrCreate(AppStorageKey.IS_LOGIN, true);console.log('检测到已登录状态,自动恢复');} else {AppStorage.setOrCreate(AppStorageKey.IS_LOGIN, false);console.log('未检测到登录状态');}} catch (error) {console.error(`读取登录状态失败:${(error as Error).message}`);AppStorage.setOrCreate(AppStorageKey.IS_LOGIN, false);}}
}

六、视图模块:MVVM的View层

6.1 路由配置(main_pages.json)

文件路径:src/main/resources/base/profile/main_pages.json

{"src": ["pages/Index","pages/LoginPage","pages/GoodsPage"]
}

配置完成后点击DevEco Studio右上角「Sync New」同步工程。

6.2 本地预设账号数据(user_info.json)

文件路径:src/main/resources/rawfile/user_info.json

{"phone": "13800138000","password": "Admin123456"
}

6.3 启动引导页(Index.ets)

// pages/Index.ets(MVVM-View层)
import { router } from '@kit.ArkUI';
import { Context } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';import AppStorageKey from '../constants/AppStorageKey';
import { LoginViewModel } from '../viewmodel/LoginViewModel';@Entry
@Component
struct Index {// 响应式登录状态(关联AppStorage):View层仅读取状态,不修改业务逻辑@StorageLink(AppStorageKey.IS_LOGIN) isLogin: boolean = false;// 应用上下文(需导入Context类型)private context: Context = this.getUIContext().getHostContext() as Context;// 初始化LoginViewModel单例实例:View层仅调用方法,不参与逻辑实现private loginVM: LoginViewModel = LoginViewModel.getInstance();build() {// View层核心:仅渲染UI,无任何业务逻辑Column() {Text('这里是启动页面哦').fontSize(28).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ bottom: 40 });}.width('100%').height('100%').backgroundColor(Color.White).justifyContent(FlexAlign.Center);}/*** 页面加载时判断登录状态,自动跳转对应页面* View层职责:调用ViewModel方法获取状态,根据状态做UI层面的路由跳转*/async aboutToAppear() {// 调用ViewModel方法:仅触发逻辑,不关心内部实现await this.loginVM.readLoginState(this.context);// 延迟执行,避免页面闪屏(纯UI体验优化,无业务逻辑)setTimeout(() => {if (this.isLogin) {// 已登录:跳转商品管理页(暂时使用router)router.replaceUrl({ url: 'pages/GoodsPage' }).catch((e: BusinessError) => {console.error('跳转商品页失败:', e.message);});} else {// 未登录:跳转登录页router.replaceUrl({ url: 'pages/LoginPage' }).catch((e: BusinessError) => {console.error('跳转登录页失败:', e.message);});}}, 1500);}
}

6.4 登录页(LoginPage.ets)

// pages/LoginPage.ets(MVVM-View层)
import { Account } from '../model/login/Account';
import { LoginViewModel } from '../viewmodel/LoginViewModel';
import { router } from '@kit.ArkUI';
import { Context } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct LoginPage {private context: Context = this.getUIContext().getHostContext() as Context;// View层仅维护UI相关状态,不维护业务状态@State phone: string = '';@State password: string = '';@State errorTip: string = '';// 仅持有ViewModel实例,调用方法private loginVM: LoginViewModel = LoginViewModel.getInstance();/*** 登录按钮点击事件:View层仅做事件转发,所有业务逻辑交给ViewModel*/async onLoginClick() {this.errorTip = '';try {// 1. 收集UI输入数据(View层职责)const account: Account = { phone: this.phone, password: this.password };// 2. 调用ViewModel方法(不关心内部如何校验、存储)this.loginVM.updateAccount(account);const result = await this.loginVM.validateLogin(this.context);// 3. 根据ViewModel返回结果处理UI(仅展示提示、跳转页面)if (result.success) {await router.replaceUrl({ url: 'pages/GoodsPage' }).catch((e: BusinessError) => {this.errorTip = `页面跳转失败:${e.message}`;});} else {this.errorTip = result.message;}} catch (error) {this.errorTip = `登录异常:${(error as Error).message}`;}}// View层核心:仅渲染UI,绑定输入/点击事件,无任何业务判断build() {Column() {Text('商品管理系统').fontSize(32).fontWeight(FontWeight.Bold).fontColor('#333333').margin({ bottom: 60 });TextInput({ placeholder: '请输入手机号', text: this.phone }).type(InputType.PhoneNumber).onChange((value) => this.phone = value).width('100%').height(48).padding(10).backgroundColor(Color.White).border({ width: 1, color: '#e5e5e5', radius: 6 }).margin({ bottom: 20 });TextInput({ placeholder: '请输入密码(8-20位含数字、字母)', text: this.password }).type(InputType.Password).onChange((value) => this.password = value).width('100%').height(48).padding(10).backgroundColor(Color.White).border({ width: 1, color: '#e5e5e5', radius: 6 }).margin({ bottom: 30 });Text(this.errorTip).fontSize(12).fontColor(Color.Red).width('100%').textAlign(TextAlign.Start).margin({ bottom: 10 });Button('登录').onClick(() => this.onLoginClick()).width('100%').height(48).backgroundColor('#007dff').fontColor(Color.White).borderRadius(6).fontSize(16);}.width('100%').height('100%').backgroundColor('#f5f5f5').padding(30).justifyContent(FlexAlign.Center);}
}

七、功能测试与问题排查

7.1 测试场景与预期结果

测试场景 预期结果
首次启动应用 启动页显示1.5秒后,自动跳转至登录页
未输入手机号点击登录 提示“请输入手机号”
输入12位数字(138001380001)点击登录 提示“手机号格式错误(11位数字,以13-9开头)”
输入正确手机号+错误密码(Admin1234567) 提示“手机号或密码错误”
输入正确账号(13800138000/Admin123456) 提示“登录成功”,跳转至商品管理页
未创建user_info.json 提示“登录失败:文件user_info.json不存在或格式错误”
登录成功后重启应用 启动页直接跳转至商品管理页(无需重新登录)
手动删除沙箱中user_account.json后重启 启动页跳转至登录页
输入手机号/密码含前后空格(如 13800138000 ViewModel自动清洗空格,正常校验不影响登录结果

7.2 常见问题排查

  1. 导入路径错误
    • 检查AppStorageKeyLoginViewModel等文件的相对导入路径(如../constants/AppStorageKey);
    • 使用DevEco Studio的“自动导入”功能(Alt+Enter)修正路径,确保路径层级与文件目录一致。
  2. Context使用错误
    • 确保导入import { Context } from '@kit.AbilityKit'
    • 预览器不支持Context和沙箱文件操作,需使用真机/模拟器测试。
  3. 路由跳转失败
    • 检查main_pages.json中页面路径与实际文件名一致(区分大小写);
    • 跳转URL需与配置完全匹配(如pages/GoodsPage而非pages/GoodsManagerPage);
    • 捕获BusinessError并打印错误信息,定位跳转失败原因。
  4. JSON文件读写失败
    • user_info.json需放在rawfile根目录,且JSON格式无语法错误(无多余逗号、引号匹配);
    • 沙箱读写失败需确认API版本(适配API12+),API12默认拥有files目录读写权限。
  5. AppStorage状态不生效
    • 确保@StorageLink绑定正确的常量键值(如AppStorageKey.IS_LOGIN);
    • 确保readLoginState方法在页面aboutToAppear生命周期中优先执行,保证状态先初始化再判断跳转。
  6. 单例ViewModel调用错误
    • 确保通过LoginViewModel.getInstance()获取实例,而非直接new LoginViewModel()(私有构造函数禁止外部实例化);
    • 避免在多个页面重复创建实例,统一通过getInstance()保证全局唯一。

当前阶段我们并不设计太多UI内容,所以并未对UI组件层做抽离。UI阶段我们会结合实际情况进行通用组件分离。

八、内容总结

  1. MVVM分层核心
    • Model层:仅定义数据结构(Account/ValidateResult),无任何业务逻辑,是整个架构的“数据载体”;
    • ViewModel层:单例模式封装所有业务逻辑(校验、存储、数据交互),作为View和Model的“中间桥梁”,不涉及UI;
    • View层:仅负责UI渲染和用户交互,通过调用ViewModel方法完成业务操作,不包含核心逻辑,实现“视图与逻辑完全解耦”;
  2. 登录流程闭环(MVVM落地体现):View层触发操作→ViewModel层处理逻辑→Model层承载数据→ViewModel同步状态→View层响应状态更新,形成完整的MVVM交互链路;
  3. 工程化规范:统一常量管理、工具类复用、目录结构设计,区分rawfile(静态预设数据)和沙箱(动态持久化数据)的使用场景,为MVVM架构提供工程化支撑;
  4. 核心技术整合:ArkTS异步编程、正则校验、Context、AppStorage等技术,均服务于MVVM层间的高效交互,而非独立存在。

九、代码仓库

  • 工程名称:ClassObjectDemo_9
  • 仓库地址:https://gitee.com/juhetianxia321/harmony-os-code-base.git

十、下集预告

本节核心落地了MVVM的分层设计思想,完成了基于单例ViewModel的登录模块开发。下一节将继续围绕MVVM架构,聚焦hilog日志工具的工程化封装(ViewModel层的日志规范),同时整合单例模式实现商品数据的全局管理(扩展Model/ViewModel层能力),重构商品模块底层架构,让MVVM思想在复杂业务场景中落地,支持折扣、满减、秒杀等促销场景的扩展开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1214326.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

零基础鸿蒙应用开发第三十二节:JSON核心基础与文件的读写 - 鸿蒙

零基础鸿蒙应用开发学习计划表 【学习目标】掌握 JSON 的核心结构、数据类型与语法规则,能独立编写合法的 JSON 数据; 熟练运用 ArkTS 内置 JSON 模块,实现对象与 JSON 字符串的序列化/反序列化; 掌握鸿蒙应用中本…

中文通用语音识别新标杆,Paraformer大模型实测表现

中文通用语音识别新标杆&#xff0c;Paraformer大模型实测表现 语音识别技术正从“能听懂”迈向“听得准、识得精、用得稳”的新阶段。在中文语音识别领域&#xff0c;阿里达摩院推出的Paraformer系列模型已悄然成为行业事实标准——它不靠堆算力&#xff0c;而以创新的非自回…

Unsloth与vLLM对比:推理部署哪个更快?实战评测

Unsloth与vLLM对比&#xff1a;推理部署哪个更快&#xff1f;实战评测 1. Unsloth&#xff1a;微调加速的开源利器 Unsloth 是一个专为大语言模型&#xff08;LLM&#xff09;微调和强化学习设计的开源框架&#xff0c;它的核心目标很实在&#xff1a;让模型训练更准、更快、…

Tampermonkey篡改猴200+插件打包下载

当Chrome商店无法访问时&#xff0c;知乎网友们摸索出了一套成熟的Tampermonkey离线安装方案。2026年初的最新教程显示&#xff0c;通过CRX文件本地部署GreasyFork脚本库组合&#xff0c;即使在无网络环境下也能搭建完整的脚本生态。 开始进入正题 油猴脚本不运行了&#xff…

2026年杭州电动升降机加工厂售后排名,固佳工业设备名列前茅

在工业自动化浪潮下,一台稳定高效的升降机是企业物流搬运与生产作业的核心动脉,关乎运营效率与安全成本。面对市场上鱼龙混杂的升降机供应商,如何找到合作案例丰富、售后可靠的优质伙伴?以下依据核心需求维度,为你…

RedCoins,一个免费的类似bluecoins的个人财务管理软件

喜欢bluecoins的人,可能会像我一样想寻找一个桌面版。由于bulecoins始终没有出桌面版,所以个人自己写了一个桌面版。 效果展示: 软件分为四栏:固定资产、流动资产、短期负债、流动负债四项,可以查看个人财务的趋…

2026年宿州水稻除草套餐五大实力品牌深度解析

一、摘要 随着农业种植技术的不断进步与绿色防控理念的深入,水稻田杂草防除工作愈发精细化、科学化。对于宿州及安徽地区的广大稻农而言,选择一套高效、安全、经济的除草套餐,是保障水稻丰产丰收的关键环节之一。当…

盘点服务不错的气液分离器工厂,汉英机器排名情况如何?

本榜单依托全维度市场调研与真实行业口碑,深度筛选出五家标杆企业,为企业选型提供客观依据,助力精准匹配适配的气液分离器供应伙伴。 TOP1 推荐:无锡汉英机器制造有限公司 推荐指数:★★★★★ | 口碑评分:国内专…

零基础鸿蒙应用开发第三十节:从同步阻塞到异步Promise并发 - 鸿蒙

零基础鸿蒙应用开发学习计划表 【学习目标】理解同步与异步的核心差异,明确异步的适用场景(耗时操作必用)。 识别回调地狱的弊端,知晓Promise诞生的核心目的。 掌握Promise的三种状态及不可逆流转规则,能通过reso…

深入浅出Activity工作流:从理论到实践,让业务流转自动化 - 指南

深入浅出Activity工作流:从理论到实践,让业务流转自动化 - 指南2026-01-25 12:15 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !im…

如何搭建公司网站?网站建设公司搭建网站有哪些步骤呀?

作为一名经历过网站建设诸多挑战的过来人&#xff0c;我将结合自身经验与行业常见流程&#xff0c;系统梳理公司网站建设的步骤与注意事项&#xff0c;帮助大家厘清实际需求&#xff0c;减少不必要的弯路。 一、明确需求与目标&#xff08;核心起点&#xff09; 1、确定网站类型…

Python 使用 subprocess 检测 Linux 用户是否存在,不存在则自动创建

一、背景说明 在 Linux 服务器自动化运维、初始化脚本或容器环境中&#xff0c;经常需要判断某个系统用户是否存在&#xff1a; 如果存在&#xff1a;直接使用如果不存在&#xff1a;自动创建用户 本文介绍如何使用 Python 的 subprocess 模块&#xff0c;调用系统命令 id 和…

全网最全10个AI论文软件,专科生轻松搞定毕业论文!

全网最全10个AI论文软件&#xff0c;专科生轻松搞定毕业论文&#xff01; AI 工具让论文写作不再难 对于专科生来说&#xff0c;撰写毕业论文往往是一个令人头疼的任务。从选题到开题&#xff0c;再到撰写和降重&#xff0c;每一个环节都可能让人感到压力山大。而随着 AI 技术…

超详细版Batocera游戏整合包配置步骤(新手友好)

以下是对您提供的博文《超详细版 Batocera 游戏整合包配置技术解析》的 深度润色与工程化重构版本 。本次优化严格遵循您的全部要求: ✅ 彻底去除AI痕迹(无模板化句式、无空洞总结、无机械连接词) ✅ 摒弃“引言/概述/核心特性/原理解析/实战指南/总结”等刻板结构 ✅ …

Qwen-Image-Edit-2511保姆级教程:从下载到出图全流程

Qwen-Image-Edit-2511保姆级教程&#xff1a;从下载到出图全流程 你是不是也遇到过这些情况&#xff1a;想把商品图里的背景换成纯白&#xff0c;结果边缘发灰&#xff1b;想给海报加一句中文标语&#xff0c;字体却和原图不搭&#xff1b;想让两张人物照片风格统一&#xff0…

深度剖析usb_burning_tool支持设备类型与兼容性

以下是对您提供的博文内容进行 深度润色与结构重构后的专业级技术文章 。整体遵循“去AI化、强人设、重实战、轻模板”的原则,摒弃所有刻板标题与套路化表达,以一位深耕嵌入式烧录领域十年的工程师口吻娓娓道来——有经验、有踩坑、有思辨、有温度,同时确保技术细节精准、…

2026山东优秀的污水提升器实力厂家

一、 核心结论 在2026年的市场环境下,地下室改造、商业空间升级及环保标准提升,共同驱动了污水提升器市场的专业化与精细化发展。为帮助山东及全国用户精准选型,本报告建立了一套涵盖产品技术、市场表现、服务能力、…

零基础也能用!YOLOv9官方镜像保姆级教程,快速实现图像识别

零基础也能用&#xff01;YOLOv9官方镜像保姆级教程&#xff0c;快速实现图像识别 你是不是也遇到过这样的情况&#xff1a;刚下载完一个目标检测镜像&#xff0c;打开终端却卡在“conda activate”命令上&#xff1f;或者复制粘贴了一堆训练命令&#xff0c;结果报错说Module…

为什么Qwen3-14B能省事?128k长文单卡推理部署解析

为什么Qwen3-14B能省事&#xff1f;128k长文单卡推理部署解析 1. 它不是“小模型”&#xff0c;而是“刚刚好”的大模型守门员 很多人看到“14B”就下意识划走——觉得参数不够大、性能不够强、跑不起来新任务。但Qwen3-14B恰恰打破了这个惯性认知&#xff1a;它不是在参数规…

Qwen3-4B-Instruct-2507企业部署:高可用架构设计案例

Qwen3-4B-Instruct-2507企业部署&#xff1a;高可用架构设计案例 1. 为什么需要企业级部署方案&#xff1f; 你可能已经试过在单卡上跑通 Qwen3-4B-Instruct-2507——输入几行提示词&#xff0c;模型秒回一段逻辑清晰、语言自然的文本&#xff0c;体验确实流畅。但当它真正走…